tftpd: introduce new option -S

Historically, tftpd disallowed write requests to existing files
that are not publicly writable. Such requirement is questionable at least.
Let us make it possible to run tftpd in chrooted environment
keeping files non-world writable.

New option -S enables write requests to existing files
for chrooted run according to generic file permissions.
It is ignored unless tftpd runs chrooted.

MFC after:	1 month
Requested by:	marck
Differential:	https://reviews.freebsd.org/D41090 (based on)
This commit is contained in:
Eugene Grosbein 2023-07-21 03:11:33 +07:00
parent 1c42ed54bf
commit 273a307d0b
2 changed files with 29 additions and 7 deletions

View File

@ -28,7 +28,7 @@
.\" @(#)tftpd.8 8.1 (Berkeley) 6/4/93
.\" $FreeBSD$
.\"
.Dd March 2, 2020
.Dd July 20, 2023
.Dt TFTPD 8
.Os
.Sh NAME
@ -72,7 +72,11 @@ Files containing the string
or starting with
.Dq Li "../"
are not allowed.
Files may be written only if they already exist and are publicly writable.
Files may be written only if they already exist (unless the
.Fl w
option is used) and are publicly writable (unless chrooted and the
.Fl S
option is used).
Note that this extends the concept of
.Dq public
to include
@ -191,6 +195,12 @@ to change its root directory to
After doing that but before accepting commands,
.Nm
will switch credentials to an unprivileged user.
.It Fl S
If
.Nm
runs chrooted, the option allows write requests according to generic
file permissions, skipping requirement for files to be publicly writable.
The option is ignored for non-chrooted run.
.It Fl u Ar user
Switch credentials to
.Ar user
@ -275,12 +285,16 @@ the
.Fl c
option was introduced in
.Fx 4.3 ,
and the
the
.Fl F
and
.Fl W
options were introduced in
.Fx 7.4 .
.Fx 7.4 ,
and the
.Fl S
option was introduced in
.Fx 13.3 .
.Pp
Support for Timeout Interval and Transfer Size Options (RFC2349)
was introduced in

View File

@ -100,6 +100,7 @@ static struct dirlist {
static int suppress_naks;
static int logging;
static int ipchroot;
static int check_woth = 1;
static int create_new = 0;
static const char *newfile_format = "%Y%m%d";
static int increase_name = 0;
@ -141,7 +142,7 @@ main(int argc, char *argv[])
acting_as_client = 0;
tftp_openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
while ((ch = getopt(argc, argv, "cCd::F:lnoOp:s:u:U:wW")) != -1) {
while ((ch = getopt(argc, argv, "cCd::F:lnoOp:sS:u:U:wW")) != -1) {
switch (ch) {
case 'c':
ipchroot = 1;
@ -181,6 +182,9 @@ main(int argc, char *argv[])
case 's':
chroot_dir = optarg;
break;
case 'S':
check_woth = -1;
break;
case 'u':
chuser = optarg;
break;
@ -361,7 +365,11 @@ main(int argc, char *argv[])
tftp_log(LOG_ERR, "setuid failed");
exit(1);
}
if (check_woth == -1)
check_woth = 0;
}
if (check_woth == -1)
check_woth = 1;
len = sizeof(me_sock);
if (getsockname(0, (struct sockaddr *)&me_sock, &len) == 0) {
@ -704,7 +712,7 @@ validate_access(int peer, char **filep, int mode)
if ((stbuf.st_mode & S_IROTH) == 0)
return (EACCESS);
} else {
if ((stbuf.st_mode & S_IWOTH) == 0)
if (check_woth && ((stbuf.st_mode & S_IWOTH) == 0))
return (EACCESS);
}
} else {
@ -734,7 +742,7 @@ validate_access(int peer, char **filep, int mode)
if ((stbuf.st_mode & S_IROTH) != 0)
break;
} else {
if ((stbuf.st_mode & S_IWOTH) != 0)
if (!check_woth || ((stbuf.st_mode & S_IWOTH) != 0))
break;
}
err = EACCESS;