Rework storing files thoroughly. This includes:

o Remove the race between stat(2) & fopen(3) when creating
  a unique file.

o Improve bound checking when generating a unique name from
  a given pathname.

o Ignore REST marker on APPE.  No RFC specifies this case,
  but the idea of resuming APPE's implies this.

o By default, deny upload resumes and appends by anonymous users.
  Previously these commands were translated to STOU silently,
  which led to broken files on server without any notification
  to the user.

o Add an option, -m, to allow anonymous users to modify
  existing files (e.g., to resume uploads) if filesystem
  permissions permit.

Portions obrainded from:	OpenBSD
MFC after:			3 weeks
This commit is contained in:
Yaroslav Tykhiy 2002-08-08 17:53:52 +00:00
parent dc0f86a25d
commit a117c34534
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=101537
2 changed files with 84 additions and 24 deletions

View File

@ -40,7 +40,7 @@
.Nd Internet File Transfer Protocol server
.Sh SYNOPSIS
.Nm
.Op Fl 46AdDEMoOrRSUv
.Op Fl 46AdDEmMoOrRSUv
.Op Fl l Op Fl l
.Op Fl a Ar address
.Op Fl p Ar file
@ -121,6 +121,11 @@ are not displayed by
by default, and may have to be enabled in
.Xr syslogd 8 Ns 's
configuration file.
.It Fl m
Permit anonymous users to overwrite or modify
existing files if allowed by filesystem permissions.
By default, anonymous users cannot modify existing files;
in particular, files to upload will be created under a unique name.
.It Fl M
Prevent anonymous users from creating directories.
.It Fl o

View File

@ -139,6 +139,7 @@ int noepsv=0; /* EPSV command is disabled. */
int noretr=0; /* RETR command is disabled. */
int noguestretr=0; /* RETR command is disabled for anon users. */
int noguestmkd=0; /* MKD command is disabled for anon users. */
int noguestmod=1; /* anon users may not modify existing files. */
static volatile sig_atomic_t recvurg;
sig_atomic_t transflag;
@ -242,7 +243,7 @@ static void dolog(struct sockaddr *);
static char *curdir(void);
static void end_login(void);
static FILE *getdatasock(char *);
static char *gunique(char *);
static int guniquefd(char *, char **);
static void lostconn(int);
static void sigquit(int);
static int receive_data(FILE *, FILE *);
@ -294,7 +295,7 @@ main(int argc, char *argv[], char **envp)
#endif /* OLD_SETPROCTITLE */
while ((ch = getopt(argc, argv, "46a:AdDElMoOp:rRSt:T:u:Uv")) != -1) {
while ((ch = getopt(argc, argv, "46a:AdDElmMoOp:rRSt:T:u:Uv")) != -1) {
switch (ch) {
case '4':
enable_v4 = 1;
@ -330,6 +331,10 @@ main(int argc, char *argv[], char **envp)
logging++; /* > 1 == extra logging */
break;
case 'm':
noguestmod = 0;
break;
case 'M':
noguestmkd = 1;
break;
@ -1587,24 +1592,45 @@ retrieve(char *cmd, char *name)
void
store(char *name, char *mode, int unique)
{
int fd;
FILE *fout, *din;
struct stat st;
int (*closefunc)(FILE *);
if ((unique || guest) && stat(name, &st) == 0 &&
(name = gunique(name)) == NULL) {
LOGCMD(*mode == 'w' ? "put" : "append", name);
return;
if (*mode == 'a') { /* APPE */
if (unique) {
/* Programming error */
syslog(LOG_ERR, "Internal: unique flag to APPE");
unique = 0;
}
if (guest && noguestmod) {
reply(550, "Appending to existing file denied");
goto err;
}
restart_point = 0; /* not affected by preceding REST */
}
if (unique) /* STOU overrides REST */
restart_point = 0;
if (guest && noguestmod) {
if (restart_point) { /* guest STOR w/REST */
reply(550, "Modifying existing file denied");
goto err;
} else /* treat guest STOR as STOU */
unique = 1;
}
if (restart_point)
mode = "r+";
fout = fopen(name, mode);
mode = "r+"; /* so ASCII manual seek can work */
if (unique) {
if ((fd = guniquefd(name, &name)) < 0)
goto err;
fout = fdopen(fd, mode);
} else
fout = fopen(name, mode);
closefunc = fclose;
if (fout == NULL) {
perror_reply(553, name);
LOGCMD(*mode == 'w' ? "put" : "append", name);
return;
goto err;
}
byte_count = -1;
if (restart_point) {
@ -1652,6 +1678,10 @@ store(char *name, char *mode, int unique)
done:
LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
(*closefunc)(fout);
return;
err:
LOGCMD(*mode == 'a' ? "append" : "put" , name);
return;
}
static FILE *
@ -2690,38 +2720,63 @@ long_passive(char *cmd, int pf)
}
/*
* Generate unique name for file with basename "local".
* The file named "local" is already known to exist.
* Generate unique name for file with basename "local"
* and open the file in order to avoid possible races.
* Try "local" first, then "local.1", "local.2" etc, up to "local.99".
* Return descriptor to the file, set "name" to its name.
*
* Generates failure reply on error.
*/
static char *
gunique(char *local)
static int
guniquefd(char *local, char **name)
{
static char new[MAXPATHLEN];
struct stat st;
int count;
char *cp;
int count;
int fd;
cp = strrchr(local, '/');
if (cp)
*cp = '\0';
if (stat(cp ? local : ".", &st) < 0) {
perror_reply(553, cp ? local : ".");
return ((char *) 0);
return (-1);
}
if (cp)
if (cp) {
/*
* Let not overwrite dirname with counter suffix.
* -4 is for /nn\0
* In this extreme case dot won't be put in front of suffix.
*/
if (strlen(local) > sizeof(new) - 4) {
reply(553, "Pathname too long");
return (-1);
}
*cp = '/';
}
/* -4 is for the .nn<null> we put on the end below */
(void) snprintf(new, sizeof(new) - 4, "%s", local);
cp = new + strlen(new);
*cp++ = '.';
for (count = 1; count < 100; count++) {
(void)sprintf(cp, "%d", count);
if (stat(new, &st) < 0)
return (new);
/*
* Don't generate dotfile unless requested explicitly.
* This covers the case when basename gets truncated off
* by buffer size.
*/
if (cp > new && cp[-1] != '/')
*cp++ = '.';
for (count = 0; count < 100; count++) {
/* At count 0 try unmodified name */
if (count)
(void)sprintf(cp, "%d", count);
if ((fd = open(count ? new : local,
O_RDWR | O_CREAT | O_EXCL, 0666)) >= 0) {
*name = count ? new : local;
return (fd);
}
}
reply(452, "Unique file name cannot be created.");
return (NULL);
return (-1);
}
/*