o Reduce path names in RRQ and WRQ packets by:

Reducing "/+./" strings to "/"
    Reducing "/[^/]+/../" to "/"

o Don't send an OACK when the result of the [RW]RQ is an error.

These changes allow tftpd to interact with pxelinux.bin from the syslinux
package.

Whilst the path reducing code doesn't properly handle situations where the
path component before the "/../" is a symlink to (say) ".", I would suggest
that it does the right thing in terms of the clients perception of what
their path string actually represents.  This seems better than using
realpath() and breaking environments where symlinks point outside of the
directory hierarchy that tftpd is configured to allow.
This commit is contained in:
Brian Somers 2004-06-21 08:01:16 +00:00
parent f46519d1c1
commit 7bc7e0c85e

View File

@ -329,6 +329,37 @@ main(int argc, char *argv[])
exit(1);
}
static void
reduce_path(char *fn)
{
char *slash, *ptr;
/* Reduce all "/+./" to "/" (just in case we've got "/./../" later */
while ((slash = strstr(fn, "/./")) != NULL) {
for (ptr = slash; ptr > fn && ptr[-1] == '/'; ptr--)
;
slash += 2;
while (*slash)
*++ptr = *++slash;
}
/* Now reduce all "/something/+../" to "/" */
while ((slash = strstr(fn, "/../")) != NULL) {
if (slash == fn)
break;
for (ptr = slash; ptr > fn && ptr[-1] == '/'; ptr--)
;
for (ptr--; ptr >= fn; ptr--)
if (*ptr == '/')
break;
if (ptr < fn)
break;
slash += 3;
while (*slash)
*++ptr = *++slash;
}
}
struct formats;
int validate_access(char **, int);
void xmitfile(struct formats *);
@ -374,7 +405,7 @@ tftp(struct tftphdr *tp, int size)
int i, first = 1, has_options = 0, ecode;
struct formats *pf;
char *filename, *mode, *option, *ccp;
char fnbuf[MAXPATHLEN];
char fnbuf[PATH_MAX], resolved_fnbuf[PATH_MAX];
cp = tp->th_stuff;
again:
@ -394,6 +425,7 @@ tftp(struct tftphdr *tp, int size)
}
memcpy(fnbuf, tp->th_stuff, i);
fnbuf[i] = '\0';
reduce_path(fnbuf);
filename = fnbuf;
if (first) {
mode = ++cp;
@ -449,7 +481,7 @@ tftp(struct tftphdr *tp, int size)
}
ecode = (*pf->f_validate)(&filename, tp->th_opcode);
if (has_options)
if (has_options && ecode == 0)
oack();
if (logging) {
char hbuf[NI_MAXHOST];