blob: 68198016cd90c4dc1f016585ca2c4530b74f8857 [file] [log] [blame]
Simon Kelley832af0b2007-01-21 20:01:28 +00001/* dnsmasq is Copyright (c) 2000-2006 Simon Kelley
2
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
5 the Free Software Foundation; version 2 dated June, 1991.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11*/
12
13#include "dnsmasq.h"
14
15#ifdef HAVE_TFTP
16
Simon Kelley1b7ecd12007-02-05 14:57:57 +000017static struct tftp_file *check_tftp_fileperm(struct daemon *daemon, ssize_t *len);
Simon Kelley832af0b2007-01-21 20:01:28 +000018static void free_transfer(struct tftp_transfer *transfer);
19static ssize_t tftp_err(int err, char *packet, char *mess, char *file);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000020static ssize_t tftp_err_oops(char *packet, char *file);
Simon Kelley832af0b2007-01-21 20:01:28 +000021static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
22static char *next(char **p, char *end);
23
24#define OP_RRQ 1
25#define OP_WRQ 2
26#define OP_DATA 3
27#define OP_ACK 4
28#define OP_ERR 5
29#define OP_OACK 6
30
31#define ERR_NOTDEF 0
32#define ERR_FNF 1
33#define ERR_PERM 2
34#define ERR_FULL 3
35#define ERR_ILL 4
36
37void tftp_request(struct listener *listen, struct daemon *daemon, time_t now)
38{
39 ssize_t len;
40 char *packet = daemon->packet;
41 char *filename, *mode, *p, *end, *opt;
Simon Kelley832af0b2007-01-21 20:01:28 +000042 struct sockaddr_in addr, peer;
43 struct msghdr msg;
44 struct cmsghdr *cmptr;
45 struct iovec iov;
46 struct ifreq ifr;
47 int is_err = 1, if_index = 0;
48 struct iname *tmp;
49 struct tftp_transfer *transfer, *t;
Simon Kelley832af0b2007-01-21 20:01:28 +000050
51 union {
52 struct cmsghdr align; /* this ensures alignment */
53#ifdef HAVE_LINUX_NETWORK
54 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
55#else
56 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
57#endif
58 } control_u;
59
60 msg.msg_controllen = sizeof(control_u);
61 msg.msg_control = control_u.control;
62 msg.msg_flags = 0;
63 msg.msg_name = &peer;
64 msg.msg_namelen = sizeof(peer);
65 msg.msg_iov = &iov;
66 msg.msg_iovlen = 1;
67
68 iov.iov_base = packet;
69 iov.iov_len = daemon->packet_buff_sz;
70
71 /* we overwrote the buffer... */
72 daemon->srv_save = NULL;
73
74 if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
75 return;
76
Simon Kelley1b7ecd12007-02-05 14:57:57 +000077 if (daemon->options & OPT_NOWILD)
78 addr = listen->iface->addr.in;
79 else
80 {
81 addr.sin_addr.s_addr = 0;
82
Simon Kelley832af0b2007-01-21 20:01:28 +000083#if defined(HAVE_LINUX_NETWORK)
Simon Kelley1b7ecd12007-02-05 14:57:57 +000084 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
85 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
86 {
87 addr.sin_addr = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
88 if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
89 }
90 if (!(ifr.ifr_ifindex = if_index) ||
91 ioctl(listen->tftpfd, SIOCGIFNAME, &ifr) == -1)
92 return;
93
Simon Kelley832af0b2007-01-21 20:01:28 +000094#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelley1b7ecd12007-02-05 14:57:57 +000095 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
96 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
97 addr.sin_addr = *((struct in_addr *)CMSG_DATA(cmptr));
98 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
99 if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
100
101 if (if_index == 0 || !if_indextoname(if_index, ifr.ifr_name))
102 return;
103
Simon Kelley832af0b2007-01-21 20:01:28 +0000104#endif
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000105
106 if (addr.sin_addr.s_addr == 0)
107 return;
108
109 if (!iface_check(daemon, AF_INET, (struct all_addr *)&addr, &ifr, &if_index))
110 return;
111
112 /* allowed interfaces are the same as for DHCP */
113 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
114 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
115 return;
116
117 }
118
Simon Kelley832af0b2007-01-21 20:01:28 +0000119 /* tell kernel to use ephemeral port */
120 addr.sin_port = 0;
121 addr.sin_family = AF_INET;
122#ifdef HAVE_SOCKADDR_SA_LEN
123 addr.sin_len = sizeof(addr);
124#endif
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000125
Simon Kelley832af0b2007-01-21 20:01:28 +0000126 if (!(transfer = malloc(sizeof(struct tftp_transfer))))
127 return;
128
129 if ((transfer->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
130 {
131 free(transfer);
132 return;
133 }
134
135 transfer->peer = peer;
136 transfer->timeout = now + 1;
137 transfer->backoff = 1;
138 transfer->block = 1;
139 transfer->blocksize = 512;
140 transfer->file = NULL;
141 transfer->opt_blocksize = transfer->opt_transize = 0;
142
143 if (bind(transfer->sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 ||
144 !fix_fd(transfer->sockfd))
145 {
146 free_transfer(transfer);
147 return;
148 }
149
150 p = packet + 2;
151 end = packet + len;
152
153 if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
154 !(filename = next(&p, end)) ||
155 !(mode = next(&p, end)) ||
156 strcasecmp(mode, "octet") != 0)
157 len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), inet_ntoa(peer.sin_addr));
158 else
159 {
160 while ((opt = next(&p, end)))
161 {
162 if (strcasecmp(opt, "blksize") == 0 &&
Simon Kelley6b010842007-02-12 20:32:07 +0000163 (opt = next(&p, end)) &&
164 !(daemon->options & OPT_TFTP_NOBLOCK))
Simon Kelley832af0b2007-01-21 20:01:28 +0000165 {
166 transfer->blocksize = atoi(opt);
167 if (transfer->blocksize < 1)
168 transfer->blocksize = 1;
169 if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
170 transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
171 transfer->opt_blocksize = 1;
172 transfer->block = 0;
173 }
174
175 if (strcasecmp(opt, "tsize") == 0 && next(&p, end))
176 {
177 transfer->opt_transize = 1;
178 transfer->block = 0;
179 }
180 }
181
182 if (daemon->tftp_prefix)
183 {
184 strncpy(daemon->namebuff, daemon->tftp_prefix, MAXDNAME);
185 if (daemon->tftp_prefix[strlen(daemon->tftp_prefix)-1] != '/' &&
186 filename[0] != '/')
187 strncat(daemon->namebuff, "/", MAXDNAME);
188 }
189 else if (filename[0] != '/')
190 strncpy(daemon->namebuff, "/", MAXDNAME);
191 else
192 daemon->namebuff[0] = 0;
193
194 strncat(daemon->namebuff, filename, MAXDNAME);
195 daemon->namebuff[MAXDNAME-1] = 0;
196
197 /* If we're doing many tranfers from the same file, only
198 open it once this saves lots of file descriptors
199 when mass-booting a big cluster, for instance. */
200 for (t = daemon->tftp_trans; t; t = t->next)
201 if (strcmp(t->file->filename, daemon->namebuff) == 0)
202 break;
203
204 if (t)
205 {
206 /* file already open */
207 transfer->file = t->file;
208 transfer->file->refcount++;
Simon Kelley832af0b2007-01-21 20:01:28 +0000209 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000210 else
211 /* check permissions and open file */
212 transfer->file = check_tftp_fileperm(daemon, &len);
213
214 if (transfer->file)
Simon Kelley832af0b2007-01-21 20:01:28 +0000215 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000216 if ((len = get_block(packet, transfer)) == -1)
217 len = tftp_err_oops(packet, daemon->namebuff);
218 else
219 is_err = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000220 }
221 }
222
223 while (sendto(transfer->sockfd, packet, len, 0,
224 (struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR);
225
226 if (is_err)
227 free_transfer(transfer);
228 else
229 {
230 syslog(LOG_INFO, _("TFTP sent %s to %s"), daemon->namebuff, inet_ntoa(peer.sin_addr));
231 transfer->next = daemon->tftp_trans;
232 daemon->tftp_trans = transfer;
233 }
234}
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000235
236static struct tftp_file *check_tftp_fileperm(struct daemon *daemon, ssize_t *len)
237{
238 char *packet = daemon->packet, *namebuff = daemon->namebuff;
239 struct tftp_file *file;
240 uid_t uid = geteuid();
241 struct stat statbuf;
242
243 /* trick to ban moving out of the subtree */
244 if (daemon->tftp_prefix && strstr(namebuff, "/../"))
245 {
246 errno = EACCES;
247 goto perm;
248 }
249
250 if (stat(namebuff, &statbuf) == -1)
251 {
252 if (errno == ENOENT || errno == ENOTDIR)
253 goto nofile;
254 else if (errno == EACCES)
255 goto perm;
256 else
257 goto oops;
258 }
259
260 /* running as root, must be world-readable */
261 if (uid == 0)
262 {
263 if (!(statbuf.st_mode & S_IROTH))
264 {
265 errno = EACCES;
266 goto perm;
267 }
268 }
269 /* in secure mode, must be owned by user running dnsmasq */
270 else if ((daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid)
271 {
272 errno = EACCES;
273 goto perm;
274 }
275
276 if (!(file = malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1)))
277 {
278 errno = ENOMEM;
279 goto oops;
280 }
281
282 if ((file->fd = open(namebuff, O_RDONLY)) == -1)
283 {
284 free(file);
285 if (errno == EACCES || errno == EISDIR)
286 goto perm;
287 else
288 goto oops;
289 }
290
291 file->size = statbuf.st_size;
292 file->refcount = 1;
293 strcpy(file->filename, namebuff);
294 return file;
295
296nofile:
297 *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
298 return NULL;
299
300 perm:
301 *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff);
302 return NULL;
303
304oops:
305 *len = tftp_err_oops(packet, namebuff);
306 return NULL;
307}
308
Simon Kelley832af0b2007-01-21 20:01:28 +0000309void check_tftp_listeners(struct daemon *daemon, fd_set *rset, time_t now)
310{
311 struct tftp_transfer *transfer, *tmp, **up;
312 ssize_t len;
313
314 struct ack {
315 unsigned short op, block;
316 } *mess = (struct ack *)daemon->packet;
317
318 /* Check for activity on any existing transfers */
319 for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
320 {
321 tmp = transfer->next;
322
323 if (FD_ISSET(transfer->sockfd, rset))
324 {
325 /* we overwrote the buffer... */
326 daemon->srv_save = NULL;
327
328 if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
329 {
330 if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
331 {
332 /* Got ack, ensure we take the (re)transmit path */
333 transfer->timeout = now;
334 transfer->backoff = 0;
335 transfer->block++;
336 }
337 else if (ntohs(mess->op) == OP_ERR)
338 {
339 char *p = daemon->packet + sizeof(struct ack);
340 char *end = daemon->packet + len;
341 char *err = next(&p, end);
342 /* Sanitise error message */
343 if (!err)
344 err = "";
345 else
346 {
347 char *q, *r;
348 for (q = r = err; *r; r++)
349 if (isprint(*r))
350 *(q++) = *r;
351 *q = 0;
352 }
353 syslog(LOG_ERR, _("TFTP error %d %s received from %s"),
354 (int)ntohs(mess->block), err,
355 inet_ntoa(transfer->peer.sin_addr));
356
357 /* Got err, ensure we take abort */
358 transfer->timeout = now;
359 transfer->backoff = 100;
360 }
361 }
362 }
363
364 if (difftime(now, transfer->timeout) >= 0.0)
365 {
366 int endcon = 0;
367
368 /* timeout, retransmit */
369 transfer->timeout += 1<<(transfer->backoff);
370
371 /* we overwrote the buffer... */
372 daemon->srv_save = NULL;
373
374 if ((len = get_block(daemon->packet, transfer)) == -1)
375 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000376 len = tftp_err_oops(daemon->packet, transfer->file->filename);
Simon Kelley832af0b2007-01-21 20:01:28 +0000377 endcon = 1;
378 }
379 else if (++transfer->backoff > 5)
380 {
381 /* don't complain about timeout when we're awaiting the last
382 ACK, some clients never send it */
383 if (len != 0)
384 syslog(LOG_ERR, _("TFTP failed sending %s to %s"),
385 transfer->file->filename, inet_ntoa(transfer->peer.sin_addr));
386 len = 0;
387 }
388
389 if (len != 0)
390 while(sendto(transfer->sockfd, daemon->packet, len, 0,
391 (struct sockaddr *)&transfer->peer, sizeof(transfer->peer)) == -1 && errno == EINTR);
392
393 if (endcon || len == 0)
394 {
395 /* unlink */
396 *up = tmp;
397 free_transfer(transfer);
398 continue;
399 }
400 }
401
402 up = &transfer->next;
403 }
404}
405
406static void free_transfer(struct tftp_transfer *transfer)
407{
408 close(transfer->sockfd);
409 if (transfer->file && (--transfer->file->refcount) == 0)
410 {
411 close(transfer->file->fd);
412 free(transfer->file);
413 }
414 free(transfer);
415}
416
417static char *next(char **p, char *end)
418{
419 char *ret = *p;
420 size_t len;
421
422 if (*(end-1) != 0 ||
423 *p == end ||
424 (len = strlen(ret)) == 0)
425 return NULL;
426
427 *p += len + 1;
428 return ret;
429}
430
431static ssize_t tftp_err(int err, char *packet, char *message, char *file)
432{
433 struct errmess {
434 unsigned short op, err;
435 char message[];
436 } *mess = (struct errmess *)packet;
437 ssize_t ret = 4;
438 char *errstr = strerror(errno);
439
440 mess->op = htons(OP_ERR);
441 mess->err = htons(err);
442 ret += (snprintf(mess->message, 500, message, file, errstr) + 1);
443 if (err != ERR_FNF)
444 syslog(LOG_ERR, "TFTP %s", mess->message);
445
446 return ret;
447}
448
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000449static ssize_t tftp_err_oops(char *packet, char *file)
450{
451 return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), file);
452}
453
Simon Kelley832af0b2007-01-21 20:01:28 +0000454/* return -1 for error, zero for done. */
455static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
456{
457 if (transfer->block == 0)
458 {
459 /* send OACK */
460 char *p;
461 struct oackmess {
462 unsigned short op;
463 char data[];
464 } *mess = (struct oackmess *)packet;
465
466 p = mess->data;
467 mess->op = htons(OP_OACK);
468 if (transfer->opt_blocksize)
469 {
470 p += (sprintf(p, "blksize") + 1);
471 p += (sprintf(p, "%d", transfer->blocksize) + 1);
472 }
473 if (transfer->opt_transize)
474 {
475 p += (sprintf(p,"tsize") + 1);
476 p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
477 }
478
479 return p - packet;
480 }
481 else
482 {
483 /* send data packet */
484 struct datamess {
485 unsigned short op, block;
486 unsigned char data[];
487 } *mess = (struct datamess *)packet;
488
489 off_t offset = transfer->blocksize * (transfer->block - 1);
490 size_t size = transfer->file->size - offset;
491
492 if (offset > transfer->file->size)
493 return 0; /* finished */
494
495 if (size > transfer->blocksize)
496 size = transfer->blocksize;
497
498 lseek(transfer->file->fd, offset, SEEK_SET);
499
500 mess->op = htons(OP_DATA);
501 mess->block = htons((unsigned short)(transfer->block));
502
503 if (!read_write(transfer->file->fd, mess->data, size, 1))
504 return -1;
505 else
506 return size + 4;
507 }
508}
509
510#endif