diff options
author | brian <brian@FreeBSD.org> | 2004-06-21 08:01:16 +0000 |
---|---|---|
committer | brian <brian@FreeBSD.org> | 2004-06-21 08:01:16 +0000 |
commit | 86ff38aa6a16b0d64c479830cee246b451837992 (patch) | |
tree | b23d0d3ee239998bb689eb262cc2b53c953a4434 | |
parent | 05c25483b4784c8f5df796f4494e20e8c408d1d6 (diff) | |
download | FreeBSD-src-86ff38aa6a16b0d64c479830cee246b451837992.zip FreeBSD-src-86ff38aa6a16b0d64c479830cee246b451837992.tar.gz |
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.
-rw-r--r-- | libexec/tftpd/tftpd.c | 36 |
1 files changed, 34 insertions, 2 deletions
diff --git a/libexec/tftpd/tftpd.c b/libexec/tftpd/tftpd.c index 7eadc9f..0067287 100644 --- a/libexec/tftpd/tftpd.c +++ b/libexec/tftpd/tftpd.c @@ -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 @@ again: } memcpy(fnbuf, tp->th_stuff, i); fnbuf[i] = '\0'; + reduce_path(fnbuf); filename = fnbuf; if (first) { mode = ++cp; @@ -449,7 +481,7 @@ option_fail: } ecode = (*pf->f_validate)(&filename, tp->th_opcode); - if (has_options) + if (has_options && ecode == 0) oack(); if (logging) { char hbuf[NI_MAXHOST]; |