Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/libexec/tftpd/tftpd.c
34822 views
1
/*-
2
* SPDX-License-Identifier: BSD-3-Clause
3
*
4
* Copyright (c) 1983, 1993
5
* The Regents of the University of California. All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
* 3. Neither the name of the University nor the names of its contributors
16
* may be used to endorse or promote products derived from this software
17
* without specific prior written permission.
18
*
19
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
* SUCH DAMAGE.
30
*/
31
32
/*
33
* Trivial file transfer protocol server.
34
*
35
* This version includes many modifications by Jim Guyton
36
* <guyton@rand-unix>.
37
*/
38
39
#include <sys/param.h>
40
#include <sys/ioctl.h>
41
#include <sys/socket.h>
42
#include <sys/stat.h>
43
#include <sys/time.h>
44
45
#include <netinet/in.h>
46
#include <arpa/tftp.h>
47
48
#include <ctype.h>
49
#include <errno.h>
50
#include <fcntl.h>
51
#include <netdb.h>
52
#include <pwd.h>
53
#include <stdint.h>
54
#include <stdio.h>
55
#include <stdlib.h>
56
#include <string.h>
57
#include <syslog.h>
58
#include <time.h>
59
#include <unistd.h>
60
61
#include "tftp-file.h"
62
#include "tftp-io.h"
63
#include "tftp-utils.h"
64
#include "tftp-transfer.h"
65
#include "tftp-options.h"
66
67
#ifdef LIBWRAP
68
#include <tcpd.h>
69
#endif
70
71
static void tftp_wrq(int peer, char *, size_t);
72
static void tftp_rrq(int peer, char *, size_t);
73
74
/*
75
* Null-terminated directory prefix list for absolute pathname requests and
76
* search list for relative pathname requests.
77
*
78
* MAXDIRS should be at least as large as the number of arguments that
79
* inetd allows (currently 20).
80
*/
81
#define MAXDIRS 20
82
static struct dirlist {
83
const char *name;
84
size_t len;
85
} dirs[MAXDIRS+1];
86
static int suppress_naks;
87
static int logging;
88
static int ipchroot;
89
static int check_woth = 1;
90
static int create_new = 0;
91
static const char *newfile_format = "%Y%m%d";
92
static int increase_name = 0;
93
static mode_t mask = S_IWGRP | S_IWOTH;
94
95
struct formats;
96
static void tftp_recvfile(int peer, const char *mode);
97
static void tftp_xmitfile(int peer, const char *mode);
98
static int validate_access(int peer, char **, int);
99
static char peername[NI_MAXHOST];
100
101
static FILE *file;
102
103
static struct formats {
104
const char *f_mode;
105
int f_convert;
106
} formats[] = {
107
{ "netascii", 1 },
108
{ "octet", 0 },
109
{ NULL, 0 }
110
};
111
112
int
113
main(int argc, char *argv[])
114
{
115
struct tftphdr *tp;
116
int peer;
117
socklen_t peerlen, len;
118
ssize_t n;
119
int ch;
120
char *chroot_dir = NULL;
121
struct passwd *nobody;
122
const char *chuser = "nobody";
123
char recvbuffer[MAXPKTSIZE];
124
int allow_ro = 1, allow_wo = 1, block = 0, on = 1;
125
pid_t pid;
126
127
tzset(); /* syslog in localtime */
128
acting_as_client = 0;
129
130
tftp_openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
131
while ((ch = getopt(argc, argv, "bcCd::F:lnoOp:s:Su:U:wW")) != -1) {
132
switch (ch) {
133
case 'b':
134
block = 1;
135
break;
136
case 'c':
137
ipchroot = 1;
138
break;
139
case 'C':
140
ipchroot = 2;
141
break;
142
case 'd':
143
if (optarg == NULL)
144
debug++;
145
else if (atoi(optarg) != 0)
146
debug += atoi(optarg);
147
else
148
debug |= debug_finds(optarg);
149
break;
150
case 'F':
151
newfile_format = optarg;
152
break;
153
case 'l':
154
logging = 1;
155
break;
156
case 'n':
157
suppress_naks = 1;
158
break;
159
case 'o':
160
options_rfc_enabled = 0;
161
break;
162
case 'O':
163
options_extra_enabled = 0;
164
break;
165
case 'p':
166
packetdroppercentage = (unsigned int)atoi(optarg);
167
tftp_log(LOG_INFO,
168
"Randomly dropping %d out of 100 packets",
169
packetdroppercentage);
170
break;
171
case 's':
172
chroot_dir = optarg;
173
break;
174
case 'S':
175
check_woth = -1;
176
break;
177
case 'u':
178
chuser = optarg;
179
break;
180
case 'U':
181
mask = strtol(optarg, NULL, 0);
182
break;
183
case 'w':
184
create_new = 1;
185
break;
186
case 'W':
187
create_new = 1;
188
increase_name = 1;
189
break;
190
default:
191
tftp_log(LOG_WARNING,
192
"ignoring unknown option -%c", ch);
193
}
194
}
195
if (optind < argc) {
196
struct dirlist *dirp;
197
198
/* Get list of directory prefixes. Skip relative pathnames. */
199
for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS];
200
optind++) {
201
if (argv[optind][0] == '/') {
202
dirp->name = argv[optind];
203
dirp->len = strlen(dirp->name);
204
dirp++;
205
}
206
}
207
}
208
else if (chroot_dir) {
209
dirs->name = "/";
210
dirs->len = 1;
211
}
212
if (ipchroot > 0 && chroot_dir == NULL) {
213
tftp_log(LOG_ERR, "-c requires -s");
214
exit(1);
215
}
216
217
umask(mask);
218
219
/* Find out who we are talking to and what we are going to do */
220
peerlen = sizeof(peer_sock);
221
n = recvfrom(0, recvbuffer, MAXPKTSIZE, block ? 0 : MSG_DONTWAIT,
222
(struct sockaddr *)&peer_sock, &peerlen);
223
if (n < 0) {
224
tftp_log(LOG_ERR, "recvfrom: %s", strerror(errno));
225
exit(1);
226
}
227
getnameinfo((struct sockaddr *)&peer_sock, peer_sock.ss_len,
228
peername, sizeof(peername), NULL, 0, NI_NUMERICHOST);
229
if ((size_t)n < 4 /* tftphdr */) {
230
tftp_log(LOG_ERR, "Rejecting %zd-byte request from %s",
231
n, peername);
232
exit(1);
233
}
234
235
if (ioctl(0, FIONBIO, &on) < 0) {
236
tftp_log(LOG_ERR, "ioctl(FIONBIO): %s", strerror(errno));
237
exit(1);
238
}
239
240
/*
241
* Now that we have read the message out of the UDP
242
* socket, we fork and exit. Thus, inetd will go back
243
* to listening to the tftp port, and the next request
244
* to come in will start up a new instance of tftpd.
245
*
246
* We do this so that inetd can run tftpd in "wait" mode.
247
* The problem with tftpd running in "nowait" mode is that
248
* inetd may get one or more successful "selects" on the
249
* tftp port before we do our receive, so more than one
250
* instance of tftpd may be started up. Worse, if tftpd
251
* break before doing the above "recvfrom", inetd would
252
* spawn endless instances, clogging the system.
253
*/
254
pid = fork();
255
if (pid < 0) {
256
tftp_log(LOG_ERR, "fork: %s", strerror(errno));
257
exit(1);
258
} else if (pid != 0) {
259
exit(0);
260
}
261
/* child */
262
263
#ifdef LIBWRAP
264
/*
265
* See if the client is allowed to talk to me.
266
* (This needs to be done before the chroot())
267
*/
268
{
269
struct request_info req;
270
271
request_init(&req, RQ_CLIENT_ADDR, peername, 0);
272
request_set(&req, RQ_DAEMON, "tftpd", 0);
273
274
if (hosts_access(&req) == 0) {
275
if (debug & DEBUG_ACCESS)
276
tftp_log(LOG_WARNING,
277
"Access denied by 'tftpd' entry "
278
"in /etc/hosts.allow");
279
280
/*
281
* Full access might be disabled, but maybe the
282
* client is allowed to do read-only access.
283
*/
284
request_set(&req, RQ_DAEMON, "tftpd-ro", 0);
285
allow_ro = hosts_access(&req);
286
287
request_set(&req, RQ_DAEMON, "tftpd-wo", 0);
288
allow_wo = hosts_access(&req);
289
290
if (allow_ro == 0 && allow_wo == 0) {
291
tftp_log(LOG_WARNING,
292
"Unauthorized access from %s", peername);
293
exit(1);
294
}
295
296
if (debug & DEBUG_ACCESS) {
297
if (allow_ro)
298
tftp_log(LOG_WARNING,
299
"But allowed readonly access "
300
"via 'tftpd-ro' entry");
301
if (allow_wo)
302
tftp_log(LOG_WARNING,
303
"But allowed writeonly access "
304
"via 'tftpd-wo' entry");
305
}
306
} else
307
if (debug & DEBUG_ACCESS)
308
tftp_log(LOG_WARNING,
309
"Full access allowed"
310
"in /etc/hosts.allow");
311
}
312
#endif
313
314
/*
315
* Since we exit here, we should do that only after the above
316
* recvfrom to keep inetd from constantly forking should there
317
* be a problem. See the above comment about system clogging.
318
*/
319
if (chroot_dir) {
320
if (ipchroot > 0) {
321
char *tempchroot;
322
struct stat sb;
323
int statret;
324
struct sockaddr_storage ss;
325
char hbuf[NI_MAXHOST];
326
327
statret = -1;
328
memcpy(&ss, &peer_sock, peer_sock.ss_len);
329
unmappedaddr((struct sockaddr_in6 *)&ss);
330
getnameinfo((struct sockaddr *)&ss, ss.ss_len,
331
hbuf, sizeof(hbuf), NULL, 0,
332
NI_NUMERICHOST);
333
asprintf(&tempchroot, "%s/%s", chroot_dir, hbuf);
334
if (ipchroot == 2)
335
statret = stat(tempchroot, &sb);
336
if (ipchroot == 1 ||
337
(statret == 0 && (sb.st_mode & S_IFDIR)))
338
chroot_dir = tempchroot;
339
}
340
/* Must get this before chroot because /etc might go away */
341
if ((nobody = getpwnam(chuser)) == NULL) {
342
tftp_log(LOG_ERR, "%s: no such user", chuser);
343
exit(1);
344
}
345
if (chroot(chroot_dir)) {
346
tftp_log(LOG_ERR, "chroot: %s: %s",
347
chroot_dir, strerror(errno));
348
exit(1);
349
}
350
if (chdir("/") != 0) {
351
tftp_log(LOG_ERR, "chdir: %s", strerror(errno));
352
exit(1);
353
}
354
if (setgroups(0, NULL) != 0) {
355
tftp_log(LOG_ERR, "setgroups failed");
356
exit(1);
357
}
358
if (setgid(nobody->pw_gid) != 0) {
359
tftp_log(LOG_ERR, "setgid failed");
360
exit(1);
361
}
362
if (setuid(nobody->pw_uid) != 0) {
363
tftp_log(LOG_ERR, "setuid failed");
364
exit(1);
365
}
366
if (check_woth == -1)
367
check_woth = 0;
368
}
369
if (check_woth == -1)
370
check_woth = 1;
371
372
len = sizeof(me_sock);
373
if (getsockname(0, (struct sockaddr *)&me_sock, &len) == 0) {
374
switch (me_sock.ss_family) {
375
case AF_INET:
376
((struct sockaddr_in *)&me_sock)->sin_port = 0;
377
break;
378
case AF_INET6:
379
((struct sockaddr_in6 *)&me_sock)->sin6_port = 0;
380
break;
381
default:
382
/* unsupported */
383
break;
384
}
385
} else {
386
memset(&me_sock, 0, sizeof(me_sock));
387
me_sock.ss_family = peer_sock.ss_family;
388
me_sock.ss_len = peer_sock.ss_len;
389
}
390
close(STDIN_FILENO);
391
close(STDOUT_FILENO);
392
close(STDERR_FILENO);
393
peer = socket(peer_sock.ss_family, SOCK_DGRAM, 0);
394
if (peer < 0) {
395
tftp_log(LOG_ERR, "socket: %s", strerror(errno));
396
exit(1);
397
}
398
if (bind(peer, (struct sockaddr *)&me_sock, me_sock.ss_len) < 0) {
399
tftp_log(LOG_ERR, "bind: %s", strerror(errno));
400
exit(1);
401
}
402
403
tp = (struct tftphdr *)recvbuffer;
404
tp->th_opcode = ntohs(tp->th_opcode);
405
if (tp->th_opcode == RRQ) {
406
if (allow_ro)
407
tftp_rrq(peer, tp->th_stuff, (size_t)n - 1);
408
else {
409
tftp_log(LOG_WARNING,
410
"%s read access denied", peername);
411
exit(1);
412
}
413
} else if (tp->th_opcode == WRQ) {
414
if (allow_wo)
415
tftp_wrq(peer, tp->th_stuff, (size_t)n - 1);
416
else {
417
tftp_log(LOG_WARNING,
418
"%s write access denied", peername);
419
exit(1);
420
}
421
} else
422
send_error(peer, EBADOP);
423
exit(1);
424
}
425
426
static void
427
reduce_path(char *fn)
428
{
429
char *slash, *ptr;
430
431
/* Reduce all "/+./" to "/" (just in case we've got "/./../" later */
432
while ((slash = strstr(fn, "/./")) != NULL) {
433
for (ptr = slash; ptr > fn && ptr[-1] == '/'; ptr--)
434
;
435
slash += 2;
436
while (*slash)
437
*++ptr = *++slash;
438
}
439
440
/* Now reduce all "/something/+../" to "/" */
441
while ((slash = strstr(fn, "/../")) != NULL) {
442
if (slash == fn)
443
break;
444
for (ptr = slash; ptr > fn && ptr[-1] == '/'; ptr--)
445
;
446
for (ptr--; ptr >= fn; ptr--)
447
if (*ptr == '/')
448
break;
449
if (ptr < fn)
450
break;
451
slash += 3;
452
while (*slash)
453
*++ptr = *++slash;
454
}
455
}
456
457
static char *
458
parse_header(int peer, char *recvbuffer, size_t size,
459
char **filename, char **mode)
460
{
461
struct formats *pf;
462
char *cp;
463
size_t i;
464
465
*mode = NULL;
466
cp = recvbuffer;
467
468
i = get_field(peer, recvbuffer, size);
469
if (i >= PATH_MAX) {
470
tftp_log(LOG_ERR, "Bad option - filename too long");
471
send_error(peer, EBADOP);
472
exit(1);
473
}
474
*filename = recvbuffer;
475
tftp_log(LOG_INFO, "Filename: '%s'", *filename);
476
cp += i;
477
478
i = get_field(peer, cp, size);
479
*mode = cp;
480
481
/* Find the file transfer mode */
482
for (; *cp; cp++)
483
if (isupper((unsigned char)*cp))
484
*cp = tolower((unsigned char)*cp);
485
for (pf = formats; pf->f_mode; pf++)
486
if (strcmp(pf->f_mode, *mode) == 0)
487
break;
488
if (pf->f_mode == NULL) {
489
tftp_log(LOG_ERR,
490
"Bad option - Unknown transfer mode (%s)", *mode);
491
send_error(peer, EBADOP);
492
exit(1);
493
}
494
tftp_log(LOG_INFO, "Mode: '%s'", *mode);
495
496
return (cp + 1);
497
}
498
499
/*
500
* WRQ - receive a file from the client
501
*/
502
void
503
tftp_wrq(int peer, char *recvbuffer, size_t size)
504
{
505
char *cp;
506
int has_options = 0, ecode;
507
char *filename, *mode;
508
char fnbuf[PATH_MAX];
509
510
cp = parse_header(peer, recvbuffer, size, &filename, &mode);
511
size -= (cp - recvbuffer) + 1;
512
513
strlcpy(fnbuf, filename, sizeof(fnbuf));
514
reduce_path(fnbuf);
515
filename = fnbuf;
516
517
if (size > 0) {
518
if (options_rfc_enabled)
519
has_options = !parse_options(peer, cp, size);
520
else
521
tftp_log(LOG_INFO, "Options found but not enabled");
522
}
523
524
ecode = validate_access(peer, &filename, WRQ);
525
if (ecode == 0) {
526
if (has_options)
527
send_oack(peer);
528
else
529
send_ack(peer, 0);
530
}
531
if (logging) {
532
tftp_log(LOG_INFO, "%s: write request for %s: %s", peername,
533
filename, errtomsg(ecode));
534
}
535
536
if (ecode) {
537
send_error(peer, ecode);
538
exit(1);
539
}
540
tftp_recvfile(peer, mode);
541
exit(0);
542
}
543
544
/*
545
* RRQ - send a file to the client
546
*/
547
void
548
tftp_rrq(int peer, char *recvbuffer, size_t size)
549
{
550
char *cp;
551
int has_options = 0, ecode;
552
char *filename, *mode;
553
char fnbuf[PATH_MAX];
554
555
cp = parse_header(peer, recvbuffer, size, &filename, &mode);
556
size -= (cp - recvbuffer) + 1;
557
558
strlcpy(fnbuf, filename, sizeof(fnbuf));
559
reduce_path(fnbuf);
560
filename = fnbuf;
561
562
if (size > 0) {
563
if (options_rfc_enabled)
564
has_options = !parse_options(peer, cp, size);
565
else
566
tftp_log(LOG_INFO, "Options found but not enabled");
567
}
568
569
ecode = validate_access(peer, &filename, RRQ);
570
if (ecode == 0) {
571
if (has_options) {
572
int n;
573
char lrecvbuffer[MAXPKTSIZE];
574
struct tftphdr *rp = (struct tftphdr *)lrecvbuffer;
575
576
send_oack(peer);
577
n = receive_packet(peer, lrecvbuffer, MAXPKTSIZE,
578
NULL, timeoutpacket);
579
if (n < 0) {
580
if (debug & DEBUG_SIMPLE)
581
tftp_log(LOG_DEBUG, "Aborting: %s",
582
rp_strerror(n));
583
return;
584
}
585
if (rp->th_opcode != ACK) {
586
if (debug & DEBUG_SIMPLE)
587
tftp_log(LOG_DEBUG,
588
"Expected ACK, got %s on OACK",
589
packettype(rp->th_opcode));
590
return;
591
}
592
}
593
}
594
595
if (logging)
596
tftp_log(LOG_INFO, "%s: read request for %s: %s", peername,
597
filename, errtomsg(ecode));
598
599
if (ecode) {
600
/*
601
* Avoid storms of naks to a RRQ broadcast for a relative
602
* bootfile pathname from a diskless Sun.
603
*/
604
if (suppress_naks && *filename != '/' && ecode == ENOTFOUND)
605
exit(0);
606
send_error(peer, ecode);
607
exit(1);
608
}
609
tftp_xmitfile(peer, mode);
610
}
611
612
/*
613
* Find the next value for YYYYMMDD.nn when the file to be written should
614
* be unique. Due to the limitations of nn, we will fail if nn reaches 100.
615
* Besides, that is four updates per hour on a file, which is kind of
616
* execessive anyway.
617
*/
618
static int
619
find_next_name(char *filename, int *fd)
620
{
621
/*
622
* GCC "knows" that we might write all of yyyymmdd plus the static
623
* elemenents in the format into into newname and thus complains
624
* unless we reduce the size. This array is still too big, but since
625
* the format is user supplied, it's not clear what a better limit
626
* value would be and this is sufficent to silence the warnings.
627
*/
628
static const int suffix_len = strlen("..00");
629
char yyyymmdd[MAXPATHLEN - suffix_len];
630
char newname[MAXPATHLEN];
631
int i, ret;
632
time_t tval;
633
size_t len, namelen;
634
struct tm lt;
635
636
/* Create the YYYYMMDD part of the filename */
637
time(&tval);
638
lt = *localtime(&tval);
639
len = strftime(yyyymmdd, sizeof(yyyymmdd), newfile_format, &lt);
640
if (len == 0) {
641
syslog(LOG_WARNING,
642
"Filename suffix too long (%zu characters maximum)",
643
sizeof(yyyymmdd) - 1);
644
return (EACCESS);
645
}
646
647
/* Make sure the new filename is not too long */
648
namelen = strlen(filename);
649
if (namelen >= sizeof(newname) - len - suffix_len) {
650
syslog(LOG_WARNING,
651
"Filename too long (%zu characters, %zu maximum)",
652
namelen,
653
sizeof(newname) - len - suffix_len - 1);
654
return (EACCESS);
655
}
656
657
/* Find the first file which doesn't exist */
658
for (i = 0; i < 100; i++) {
659
ret = snprintf(newname, sizeof(newname), "%s.%s.%02d",
660
filename, yyyymmdd, i);
661
/*
662
* Size checked above so this can't happen, we'd use a
663
* (void) cast, but gcc intentionally ignores that if
664
* snprintf has __attribute__((warn_unused_result)).
665
*/
666
if (ret < 0 || (size_t)ret >= sizeof(newname))
667
__unreachable();
668
*fd = open(newname, O_WRONLY | O_CREAT | O_EXCL, 0666);
669
if (*fd > 0)
670
return 0;
671
}
672
673
return (EEXIST);
674
}
675
676
/*
677
* Validate file access. Since we
678
* have no uid or gid, for now require
679
* file to exist and be publicly
680
* readable/writable.
681
* If we were invoked with arguments
682
* from inetd then the file must also be
683
* in one of the given directory prefixes.
684
* Note also, full path name must be
685
* given as we have no login directory.
686
*/
687
int
688
validate_access(int peer, char **filep, int mode)
689
{
690
static char pathname[MAXPATHLEN];
691
struct stat sb;
692
struct dirlist *dirp;
693
char *filename = *filep;
694
int err, fd;
695
696
/*
697
* Prevent tricksters from getting around the directory restrictions
698
*/
699
if (strncmp(filename, "../", 3) == 0 ||
700
strstr(filename, "/../") != NULL)
701
return (EACCESS);
702
703
if (*filename == '/') {
704
/*
705
* Absolute file name: allow the request if it's in one of the
706
* approved locations.
707
*/
708
for (dirp = dirs; dirp->name != NULL; dirp++) {
709
if (dirp->len == 1)
710
/* Only "/" can have len 1 */
711
break;
712
if (strncmp(filename, dirp->name, dirp->len) == 0 &&
713
filename[dirp->len] == '/')
714
break;
715
}
716
/* If directory list is empty, allow access to any file */
717
if (dirp->name == NULL && dirp != dirs)
718
return (EACCESS);
719
if (stat(filename, &sb) != 0)
720
return (errno == ENOENT ? ENOTFOUND : EACCESS);
721
if (!S_ISREG(sb.st_mode))
722
return (ENOTFOUND);
723
if (mode == RRQ) {
724
if ((sb.st_mode & S_IROTH) == 0)
725
return (EACCESS);
726
} else {
727
if (check_woth && (sb.st_mode & S_IWOTH) == 0)
728
return (EACCESS);
729
}
730
} else {
731
/*
732
* Relative file name: search the approved locations for it.
733
* If the file exists in one of the directories and isn't
734
* readable, continue looking. However, change the error code
735
* to give an indication that the file exists.
736
*/
737
err = ENOTFOUND;
738
for (dirp = dirs; dirp->name != NULL; dirp++) {
739
snprintf(pathname, sizeof(pathname), "%s/%s",
740
dirp->name, filename);
741
if (stat(pathname, &sb) != 0)
742
continue;
743
if (!S_ISREG(sb.st_mode))
744
continue;
745
err = EACCESS;
746
if (mode == RRQ) {
747
if ((sb.st_mode & S_IROTH) == 0)
748
continue;
749
} else {
750
if (check_woth && (sb.st_mode & S_IWOTH) == 0)
751
continue;
752
}
753
break;
754
}
755
if (dirp->name != NULL)
756
*filep = filename = pathname;
757
else if (mode == RRQ)
758
return (err);
759
else if (err != ENOTFOUND || !create_new)
760
return (err);
761
}
762
763
/*
764
* This option is handled here because it (might) require(s) the
765
* size of the file.
766
*/
767
option_tsize(peer, NULL, mode, &sb);
768
769
if (mode == RRQ) {
770
fd = open(filename, O_RDONLY);
771
} else if (create_new) {
772
if (increase_name) {
773
err = find_next_name(filename, &fd);
774
if (err > 0)
775
return (err + 100);
776
} else {
777
fd = open(filename,
778
O_WRONLY | O_TRUNC | O_CREAT,
779
S_IRUSR | S_IWUSR | S_IRGRP |
780
S_IWGRP | S_IROTH | S_IWOTH );
781
}
782
} else {
783
fd = open(filename, O_WRONLY | O_TRUNC);
784
}
785
if (fd < 0)
786
return (errno + 100);
787
file = fdopen(fd, mode == RRQ ? "r" : "w");
788
if (file == NULL) {
789
close(fd);
790
return (errno + 100);
791
}
792
return (0);
793
}
794
795
static void
796
tftp_xmitfile(int peer, const char *mode)
797
{
798
uint16_t block;
799
time_t now;
800
struct tftp_stats ts;
801
802
memset(&ts, 0, sizeof(ts));
803
now = time(NULL);
804
if (debug & DEBUG_SIMPLE)
805
tftp_log(LOG_DEBUG, "Transmitting file");
806
807
read_init(0, file, mode);
808
block = 1;
809
tftp_send(peer, &block, &ts);
810
read_close();
811
if (debug & DEBUG_SIMPLE)
812
tftp_log(LOG_INFO, "Sent %jd bytes in %jd seconds",
813
(intmax_t)ts.amount, (intmax_t)time(NULL) - now);
814
}
815
816
static void
817
tftp_recvfile(int peer, const char *mode)
818
{
819
uint16_t block;
820
struct timeval now1, now2;
821
struct tftp_stats ts;
822
823
gettimeofday(&now1, NULL);
824
if (debug & DEBUG_SIMPLE)
825
tftp_log(LOG_DEBUG, "Receiving file");
826
827
write_init(0, file, mode);
828
829
block = 0;
830
tftp_receive(peer, &block, &ts, NULL, 0);
831
832
gettimeofday(&now2, NULL);
833
834
if (debug & DEBUG_SIMPLE) {
835
double f;
836
if (now1.tv_usec > now2.tv_usec) {
837
now2.tv_usec += 1000000;
838
now2.tv_sec--;
839
}
840
841
f = now2.tv_sec - now1.tv_sec +
842
(now2.tv_usec - now1.tv_usec) / 100000.0;
843
tftp_log(LOG_INFO,
844
"Download of %jd bytes in %d blocks completed after %0.1f seconds\n",
845
(intmax_t)ts.amount, block, f);
846
}
847
848
return;
849
}
850
851