ftpd: Make send_dirline() more robust

Account for large file names.

Update #3530.
This commit is contained in:
Sebastian Huber
2018-10-30 09:35:06 +01:00
parent 7e2aabd751
commit 706802f870

View File

@@ -1132,42 +1132,42 @@ command_store(FTPD_SessionInfo_t *info, char const *filename)
* buf - buffer for temporary data * buf - buffer for temporary data
* *
* Output parameters: * Output parameters:
* returns 0 on failure, 1 on success * returns false on failure, true on success
* *
*/ */
static int static bool
send_dirline(int s, int wide, time_t curTime, char const* path, send_dirline(int s, bool wide, time_t curTime, char const* path,
char const* add, char const* fname, char* buf) char const* add, char const* fname, char* buf)
{ {
if(wide) struct stat stat_buf;
{ size_t plen = strlen(path);
struct stat stat_buf; size_t alen = strlen(add);
int slen = 0;
int plen = strlen(path); if(plen == 0)
int alen = strlen(add); {
if(plen == 0) buf[plen++] = '/';
buf[plen] = '\0';
}
else
{
buf = memcpy(buf, path, plen + 1);
if(alen > 0 && buf[plen - 1] != '/')
{ {
buf[plen++] = '/'; buf[plen++] = '/';
if(plen >= FTPD_BUFSIZE)
return 0;
buf[plen] = '\0'; buf[plen] = '\0';
} }
else }
{ if(plen + alen >= FTPD_BUFSIZE)
strcpy(buf, path); return 0;
if(alen > 0 && buf[plen - 1] != '/') memcpy(buf + plen, add, alen + 1);
{
buf[plen++] = '/';
if(plen >= FTPD_BUFSIZE)
return 0;
buf[plen] = '\0';
}
}
if(plen + alen >= FTPD_BUFSIZE)
return 0;
strcpy(buf + plen, add);
if (stat(buf, &stat_buf) == 0) if (stat(buf, &stat_buf) == 0)
{
if (wide)
{ {
int len;
struct tm bt; struct tm bt;
time_t tf = stat_buf.st_mtime; time_t tf = stat_buf.st_mtime;
enum { SIZE = 80 }; enum { SIZE = 80 };
@@ -1179,7 +1179,7 @@ send_dirline(int s, int wide, time_t curTime, char const* path,
else else
strftime (timeBuf, SIZE, "%b %d %H:%M", &bt); strftime (timeBuf, SIZE, "%b %d %H:%M", &bt);
len = snprintf(buf, FTPD_BUFSIZE, slen = snprintf(buf, FTPD_BUFSIZE,
"%c%c%c%c%c%c%c%c%c%c 1 %5d %5d %11u %s %s\r\n", "%c%c%c%c%c%c%c%c%c%c 1 %5d %5d %11u %s %s\r\n",
(S_ISLNK(stat_buf.st_mode)?('l'): (S_ISLNK(stat_buf.st_mode)?('l'):
(S_ISDIR(stat_buf.st_mode)?('d'):('-'))), (S_ISDIR(stat_buf.st_mode)?('d'):('-'))),
@@ -1198,18 +1198,33 @@ send_dirline(int s, int wide, time_t curTime, char const* path,
timeBuf, timeBuf,
fname fname
); );
}
if(send(s, buf, len, 0) != len) else
return 0; {
slen = snprintf(buf, FTPD_BUFSIZE, "%s\r\n", fname);
} }
} }
else else
{ {
int len = snprintf(buf, FTPD_BUFSIZE, "%s\r\n", fname); slen = snprintf(buf, FTPD_BUFSIZE, "%s: %s.\r\n", fname, strerror(errno));
if(send(s, buf, len, 0) != len)
return 0;
} }
return 1;
if (slen >= FTPD_BUFSIZE)
{
static const char dots[] = { '.', '.', '.', '\r', '\n' };
/*
* The file name exceeds the send buffer, indicate this with a ... at the
* end of the line.
*/
slen = FTPD_BUFSIZE - 1;
memcpy(&buf[slen - sizeof(dots)], dots, sizeof(dots));
}
if (slen > 0 && send(s, buf, (size_t) slen, 0) != slen)
return false;
return true;
} }
/* /*
@@ -1225,7 +1240,7 @@ send_dirline(int s, int wide, time_t curTime, char const* path,
* NONE * NONE
*/ */
static void static void
command_list(FTPD_SessionInfo_t *info, char const *fname, int wide) command_list(FTPD_SessionInfo_t *info, char const *fname, bool wide)
{ {
int s; int s;
DIR *dirp = 0; DIR *dirp = 0;
@@ -1233,7 +1248,7 @@ command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
struct stat stat_buf; struct stat stat_buf;
char buf[FTPD_BUFSIZE]; char buf[FTPD_BUFSIZE];
time_t curTime; time_t curTime;
int sc = 1; bool ok = true;
if(!info->auth) if(!info->auth)
{ {
@@ -1269,14 +1284,14 @@ command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
{ {
time(&curTime); time(&curTime);
if(!dirp && *fname) if(!dirp && *fname)
sc = sc && send_dirline(s, wide, curTime, fname, "", fname, buf); ok = ok && send_dirline(s, wide, curTime, fname, "", fname, buf);
else { else {
/* FIXME: need "." and ".." only when '-a' option is given */ /* FIXME: need "." and ".." only when '-a' option is given */
sc = sc && send_dirline(s, wide, curTime, fname, "", ".", buf); ok = ok && send_dirline(s, wide, curTime, fname, "", ".", buf);
sc = sc && send_dirline(s, wide, curTime, fname, ok = ok && send_dirline(s, wide, curTime, fname,
(strcmp(fname, ftpd_root) ? ".." : ""), "..", buf); (strcmp(fname, ftpd_root) ? ".." : ""), "..", buf);
while (sc && (dp = readdir(dirp)) != NULL) while (ok && (dp = readdir(dirp)) != NULL)
sc = sc && ok = ok &&
send_dirline(s, wide, curTime, fname, dp->d_name, dp->d_name, buf); send_dirline(s, wide, curTime, fname, dp->d_name, dp->d_name, buf);
} }
} }
@@ -1285,7 +1300,7 @@ command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
closedir(dirp); closedir(dirp);
close_data_socket(info); close_data_socket(info);
if(sc) if (ok)
send_reply(info, 226, "Transfer complete."); send_reply(info, 226, "Transfer complete.");
else else
send_reply(info, 426, "Connection aborted."); send_reply(info, 426, "Connection aborted.");
@@ -1694,11 +1709,11 @@ exec_command(FTPD_SessionInfo_t *info, char *cmd, char *args)
} }
else if (!strcmp("LIST", cmd)) else if (!strcmp("LIST", cmd))
{ {
command_list(info, args, 1); command_list(info, args, true);
} }
else if (!strcmp("NLST", cmd)) else if (!strcmp("NLST", cmd))
{ {
command_list(info, args, 0); command_list(info, args, false);
} }
else if (!strcmp("MDTM", cmd)) else if (!strcmp("MDTM", cmd))
{ {