blob: 28b63582b1ff212c1086f8b964f48b023130eb2e [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
Simon Kelleyf2621c72007-04-29 19:47:21 +0100109 if (!iface_check(daemon, AF_INET, (struct all_addr *)&addr.sin_addr,
110 &ifr, &if_index))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000111 return;
112
113 /* allowed interfaces are the same as for DHCP */
114 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
115 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
116 return;
117
118 }
119
Simon Kelley832af0b2007-01-21 20:01:28 +0000120 /* tell kernel to use ephemeral port */
121 addr.sin_port = 0;
122 addr.sin_family = AF_INET;
123#ifdef HAVE_SOCKADDR_SA_LEN
124 addr.sin_len = sizeof(addr);
125#endif
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000126
Simon Kelley832af0b2007-01-21 20:01:28 +0000127 if (!(transfer = malloc(sizeof(struct tftp_transfer))))
128 return;
129
130 if ((transfer->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
131 {
132 free(transfer);
133 return;
134 }
135
136 transfer->peer = peer;
137 transfer->timeout = now + 1;
138 transfer->backoff = 1;
139 transfer->block = 1;
140 transfer->blocksize = 512;
141 transfer->file = NULL;
142 transfer->opt_blocksize = transfer->opt_transize = 0;
143
144 if (bind(transfer->sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 ||
145 !fix_fd(transfer->sockfd))
146 {
147 free_transfer(transfer);
148 return;
149 }
150
151 p = packet + 2;
152 end = packet + len;
153
154 if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
155 !(filename = next(&p, end)) ||
156 !(mode = next(&p, end)) ||
157 strcasecmp(mode, "octet") != 0)
158 len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), inet_ntoa(peer.sin_addr));
159 else
160 {
161 while ((opt = next(&p, end)))
162 {
163 if (strcasecmp(opt, "blksize") == 0 &&
Simon Kelley6b010842007-02-12 20:32:07 +0000164 (opt = next(&p, end)) &&
165 !(daemon->options & OPT_TFTP_NOBLOCK))
Simon Kelley832af0b2007-01-21 20:01:28 +0000166 {
167 transfer->blocksize = atoi(opt);
168 if (transfer->blocksize < 1)
169 transfer->blocksize = 1;
170 if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
171 transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
172 transfer->opt_blocksize = 1;
173 transfer->block = 0;
174 }
175
176 if (strcasecmp(opt, "tsize") == 0 && next(&p, end))
177 {
178 transfer->opt_transize = 1;
179 transfer->block = 0;
180 }
181 }
182
Simon Kelleyf2621c72007-04-29 19:47:21 +0100183 strcpy(daemon->namebuff, "/");
Simon Kelley832af0b2007-01-21 20:01:28 +0000184 if (daemon->tftp_prefix)
185 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100186 if (daemon->tftp_prefix[0] == '/')
187 daemon->namebuff[0] = 0;
188 strncat(daemon->namebuff, daemon->tftp_prefix, MAXDNAME);
189 if (daemon->tftp_prefix[strlen(daemon->tftp_prefix)-1] != '/')
Simon Kelley832af0b2007-01-21 20:01:28 +0000190 strncat(daemon->namebuff, "/", MAXDNAME);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100191
192 /* Absolute pathnames OK if they match prefix */
193 if (filename[0] == '/')
194 {
195 if (strstr(filename, daemon->namebuff) == filename)
196 daemon->namebuff[0] = 0;
197 else
198 filename++;
199 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000200 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100201 else if (filename[0] == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +0000202 daemon->namebuff[0] = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000203 strncat(daemon->namebuff, filename, MAXDNAME);
204 daemon->namebuff[MAXDNAME-1] = 0;
205
206 /* If we're doing many tranfers from the same file, only
207 open it once this saves lots of file descriptors
208 when mass-booting a big cluster, for instance. */
209 for (t = daemon->tftp_trans; t; t = t->next)
210 if (strcmp(t->file->filename, daemon->namebuff) == 0)
211 break;
212
213 if (t)
214 {
215 /* file already open */
216 transfer->file = t->file;
217 transfer->file->refcount++;
Simon Kelley832af0b2007-01-21 20:01:28 +0000218 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000219 else
220 /* check permissions and open file */
221 transfer->file = check_tftp_fileperm(daemon, &len);
222
223 if (transfer->file)
Simon Kelley832af0b2007-01-21 20:01:28 +0000224 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000225 if ((len = get_block(packet, transfer)) == -1)
226 len = tftp_err_oops(packet, daemon->namebuff);
227 else
228 is_err = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000229 }
230 }
231
232 while (sendto(transfer->sockfd, packet, len, 0,
233 (struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR);
234
235 if (is_err)
236 free_transfer(transfer);
237 else
238 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100239 my_syslog(LOG_INFO, _("TFTP sent %s to %s"), daemon->namebuff, inet_ntoa(peer.sin_addr));
Simon Kelley832af0b2007-01-21 20:01:28 +0000240 transfer->next = daemon->tftp_trans;
241 daemon->tftp_trans = transfer;
242 }
243}
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000244
245static struct tftp_file *check_tftp_fileperm(struct daemon *daemon, ssize_t *len)
246{
247 char *packet = daemon->packet, *namebuff = daemon->namebuff;
248 struct tftp_file *file;
249 uid_t uid = geteuid();
250 struct stat statbuf;
251
252 /* trick to ban moving out of the subtree */
253 if (daemon->tftp_prefix && strstr(namebuff, "/../"))
254 {
255 errno = EACCES;
256 goto perm;
257 }
258
259 if (stat(namebuff, &statbuf) == -1)
260 {
261 if (errno == ENOENT || errno == ENOTDIR)
262 goto nofile;
263 else if (errno == EACCES)
264 goto perm;
265 else
266 goto oops;
267 }
268
269 /* running as root, must be world-readable */
270 if (uid == 0)
271 {
272 if (!(statbuf.st_mode & S_IROTH))
273 {
274 errno = EACCES;
275 goto perm;
276 }
277 }
278 /* in secure mode, must be owned by user running dnsmasq */
279 else if ((daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid)
280 {
281 errno = EACCES;
282 goto perm;
283 }
284
285 if (!(file = malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1)))
286 {
287 errno = ENOMEM;
288 goto oops;
289 }
290
291 if ((file->fd = open(namebuff, O_RDONLY)) == -1)
292 {
293 free(file);
294 if (errno == EACCES || errno == EISDIR)
295 goto perm;
296 else
297 goto oops;
298 }
299
300 file->size = statbuf.st_size;
301 file->refcount = 1;
302 strcpy(file->filename, namebuff);
303 return file;
304
305nofile:
306 *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
307 return NULL;
308
309 perm:
310 *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff);
311 return NULL;
312
313oops:
314 *len = tftp_err_oops(packet, namebuff);
315 return NULL;
316}
317
Simon Kelley832af0b2007-01-21 20:01:28 +0000318void check_tftp_listeners(struct daemon *daemon, fd_set *rset, time_t now)
319{
320 struct tftp_transfer *transfer, *tmp, **up;
321 ssize_t len;
322
323 struct ack {
324 unsigned short op, block;
325 } *mess = (struct ack *)daemon->packet;
326
327 /* Check for activity on any existing transfers */
328 for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
329 {
330 tmp = transfer->next;
331
332 if (FD_ISSET(transfer->sockfd, rset))
333 {
334 /* we overwrote the buffer... */
335 daemon->srv_save = NULL;
336
337 if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
338 {
339 if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
340 {
341 /* Got ack, ensure we take the (re)transmit path */
342 transfer->timeout = now;
343 transfer->backoff = 0;
344 transfer->block++;
345 }
346 else if (ntohs(mess->op) == OP_ERR)
347 {
348 char *p = daemon->packet + sizeof(struct ack);
349 char *end = daemon->packet + len;
350 char *err = next(&p, end);
351 /* Sanitise error message */
352 if (!err)
353 err = "";
354 else
355 {
356 char *q, *r;
357 for (q = r = err; *r; r++)
358 if (isprint(*r))
359 *(q++) = *r;
360 *q = 0;
361 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100362 my_syslog(LOG_ERR, _("TFTP error %d %s received from %s"),
363 (int)ntohs(mess->block), err,
364 inet_ntoa(transfer->peer.sin_addr));
Simon Kelley832af0b2007-01-21 20:01:28 +0000365
366 /* Got err, ensure we take abort */
367 transfer->timeout = now;
368 transfer->backoff = 100;
369 }
370 }
371 }
372
373 if (difftime(now, transfer->timeout) >= 0.0)
374 {
375 int endcon = 0;
376
377 /* timeout, retransmit */
378 transfer->timeout += 1<<(transfer->backoff);
379
380 /* we overwrote the buffer... */
381 daemon->srv_save = NULL;
382
383 if ((len = get_block(daemon->packet, transfer)) == -1)
384 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000385 len = tftp_err_oops(daemon->packet, transfer->file->filename);
Simon Kelley832af0b2007-01-21 20:01:28 +0000386 endcon = 1;
387 }
388 else if (++transfer->backoff > 5)
389 {
390 /* don't complain about timeout when we're awaiting the last
391 ACK, some clients never send it */
392 if (len != 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100393 my_syslog(LOG_ERR, _("TFTP failed sending %s to %s"),
394 transfer->file->filename, inet_ntoa(transfer->peer.sin_addr));
Simon Kelley832af0b2007-01-21 20:01:28 +0000395 len = 0;
396 }
397
398 if (len != 0)
399 while(sendto(transfer->sockfd, daemon->packet, len, 0,
400 (struct sockaddr *)&transfer->peer, sizeof(transfer->peer)) == -1 && errno == EINTR);
401
402 if (endcon || len == 0)
403 {
404 /* unlink */
405 *up = tmp;
406 free_transfer(transfer);
407 continue;
408 }
409 }
410
411 up = &transfer->next;
412 }
413}
414
415static void free_transfer(struct tftp_transfer *transfer)
416{
417 close(transfer->sockfd);
418 if (transfer->file && (--transfer->file->refcount) == 0)
419 {
420 close(transfer->file->fd);
421 free(transfer->file);
422 }
423 free(transfer);
424}
425
426static char *next(char **p, char *end)
427{
428 char *ret = *p;
429 size_t len;
430
431 if (*(end-1) != 0 ||
432 *p == end ||
433 (len = strlen(ret)) == 0)
434 return NULL;
435
436 *p += len + 1;
437 return ret;
438}
439
440static ssize_t tftp_err(int err, char *packet, char *message, char *file)
441{
442 struct errmess {
443 unsigned short op, err;
444 char message[];
445 } *mess = (struct errmess *)packet;
446 ssize_t ret = 4;
447 char *errstr = strerror(errno);
448
449 mess->op = htons(OP_ERR);
450 mess->err = htons(err);
451 ret += (snprintf(mess->message, 500, message, file, errstr) + 1);
452 if (err != ERR_FNF)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100453 my_syslog(LOG_ERR, "TFTP %s", mess->message);
Simon Kelley832af0b2007-01-21 20:01:28 +0000454
455 return ret;
456}
457
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000458static ssize_t tftp_err_oops(char *packet, char *file)
459{
460 return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), file);
461}
462
Simon Kelley832af0b2007-01-21 20:01:28 +0000463/* return -1 for error, zero for done. */
464static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
465{
466 if (transfer->block == 0)
467 {
468 /* send OACK */
469 char *p;
470 struct oackmess {
471 unsigned short op;
472 char data[];
473 } *mess = (struct oackmess *)packet;
474
475 p = mess->data;
476 mess->op = htons(OP_OACK);
477 if (transfer->opt_blocksize)
478 {
479 p += (sprintf(p, "blksize") + 1);
480 p += (sprintf(p, "%d", transfer->blocksize) + 1);
481 }
482 if (transfer->opt_transize)
483 {
484 p += (sprintf(p,"tsize") + 1);
485 p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
486 }
487
488 return p - packet;
489 }
490 else
491 {
492 /* send data packet */
493 struct datamess {
494 unsigned short op, block;
495 unsigned char data[];
496 } *mess = (struct datamess *)packet;
497
498 off_t offset = transfer->blocksize * (transfer->block - 1);
499 size_t size = transfer->file->size - offset;
500
501 if (offset > transfer->file->size)
502 return 0; /* finished */
503
504 if (size > transfer->blocksize)
505 size = transfer->blocksize;
506
507 lseek(transfer->file->fd, offset, SEEK_SET);
508
509 mess->op = htons(OP_DATA);
510 mess->block = htons((unsigned short)(transfer->block));
511
512 if (!read_write(transfer->file->fd, mess->data, size, 1))
513 return -1;
514 else
515 return size + 4;
516 }
517}
518
519#endif