* Validate file access. Since we
* have no uid or gid, for now require
* file to exist and be publicly
* readable/writable.
* If we were invoked with arguments
* from inetd then the file must also be
* in one of the given directory prefixes.
* Note also, full path name must be
* given as we have no login directory.
*/
int
validate_access (char **filep, int mode)
{
struct stat stbuf;
int fd;
struct dirlist *dirp;
static char *pathname = 0;
char *filename = *filep;
/*
* Prevent tricksters from getting around the directory restrictions
*/
if (strstr (filename, "/../"))
return (EACCESS);
if (*filename == '/')
{
/*
* Allow the request if it's in one of the approved locations.
* Special case: check the null prefix ("/") by looking
* for length = 1 and relying on the arg. processing that
* it's a /.
*/
for (dirp = dirs; dirp->name != NULL; dirp++)
{
if (dirp->len == 1 ||
(!strncmp (filename, dirp->name, dirp->len) &&
filename[dirp->len] == '/'))
break;
}
/* If directory list is empty, allow access to any file */
if (dirp->name == NULL && dirp != dirs)
return (EACCESS);
if (stat (filename, &stbuf) < 0)
return (errno == ENOENT ? ENOTFOUND : EACCESS);
if ((stbuf.st_mode & S_IFMT) != S_IFREG)
return (ENOTFOUND);
if (mode == RRQ)
{
if ((stbuf.st_mode & S_IROTH) == 0)
return (EACCESS);
}
else
{
if ((stbuf.st_mode & S_IWOTH) == 0)
return (EACCESS);
}
}
else
{
int err;
/*
* Relative file name: search the approved locations for it.
* Don't allow write requests or ones that avoid directory
* restrictions. 这里不支持写请求,去掉即可
*/
// if (mode != RRQ || !strncmp (filename, "../", 3))
if ( !strncmp (filename, "../", 3))
return (EACCESS);
/*
* If the file exists in one of the directories and isn't
* readable, continue looking. However, change the error code
* to give an indication that the file exists.
*/
err = ENOTFOUND;
for (dirp = dirs; dirp->name != NULL; dirp++)
{
free (pathname);
pathname = malloc (strlen (dirp->name) + 1 + strlen (filename) + 1);
if (!pathname)
return ENOMEM;
sprintf (pathname, "%s/%s", dirp->name, filename);
if (stat (pathname, &stbuf) == 0 &&
(stbuf.st_mode & S_IFMT) == S_IFREG)
{
if ((stbuf.st_mode & S_IROTH) != 0)
{
break;
}
err = EACCESS;
}
}
/*
* 这里要加上读请求才返回错误,如果是写请求直接创建文件
*/
{
if (dirp->name == NULL)
return (err);
}
// if (dirp->name == NULL)
// return (err);
*filep = filename = pathname;
}
/*
* 这里加上创建文件标志
*/
fd = open (filename, mode == RRQ ? O_RDONLY : ( O_CREAT |O_WRONLY | O_TRUNC),0777);
// fd = open (filename, mode == RRQ ? O_RDONLY : (O_WRONLY | O_TRUNC));if (fd < 0)
return (errno + 100);
file = fdopen (fd, (mode == RRQ) ? "r" : "w");
if (file == NULL)
{
return errno + 100;
}
return (0);
}
/*
* Send a nak packet (error message).
* Error code passed in is one of the
* standard TFTP codes, or a UNIX errno
* offset by 100.
*/
static void
nak (int error)
{
register struct tftphdr *tp;
int length;
register struct errmsg *pe;
tp = (struct tftphdr *) buf;
tp->th_opcode = htons ((unsigned short) ERROR);
tp->th_code = htons ((unsigned short) error);
for (pe = errmsgs; pe->e_code >= 0; pe++)
if (pe->e_code == error)
break;
if (pe->e_code < 0)
{
pe->e_msg = strerror (error - 100);
tp->th_code = EUNDEF; /* set 'undef' errorcode */
}
/*
* 这里要改strcpy为memcpy,不然会溢出。
*/
length = strlen (pe->e_msg);
memcpy (tp->th_msg, pe->e_msg,length);
// strcpy (tp->th_msg, pe->e_msg);
// length = strlen (pe->e_msg);
tp->th_msg[length] = '\0';
length += 5;
if (sendto (peer, buf, length, 0, (struct sockaddr *) &from, fromlen) != length)
syslog (LOG_ERR, "nak: %m\n");
}
root@ch-HP:~# tftp 127.0.0.1
tftp> put test
Sent 7 bytes in 0.0 seconds
tftp> put test5
Sent 13 bytes in 0.0 seconds
tftp> q
root@ch-HP:~#