diff options
author | Otto Moerbeek <otto@cvs.openbsd.org> | 2005-10-12 06:50:43 +0000 |
---|---|---|
committer | Otto Moerbeek <otto@cvs.openbsd.org> | 2005-10-12 06:50:43 +0000 |
commit | 65195695f7749bcb0061ad81b0f68daccbc53243 (patch) | |
tree | 72346cbe46a1be6816aafdfb02a97f35e37f7d6f /usr.bin | |
parent | 7417ea743b8d22bdc39685adfd3857d9f56e9d74 (diff) |
Fix mget directory traversal vulnerability. From NetBSD. CAN-2002-1345.
ok millert@ deraadt@, prodding by david@
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/ftp/cmds.c | 18 | ||||
-rw-r--r-- | usr.bin/ftp/extern.h | 3 | ||||
-rw-r--r-- | usr.bin/ftp/util.c | 38 |
3 files changed, 52 insertions, 7 deletions
diff --git a/usr.bin/ftp/cmds.c b/usr.bin/ftp/cmds.c index f316f3cdc9a..eda879e1f2c 100644 --- a/usr.bin/ftp/cmds.c +++ b/usr.bin/ftp/cmds.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmds.c,v 1.48 2004/09/16 04:39:16 deraadt Exp $ */ +/* $OpenBSD: cmds.c,v 1.49 2005/10/12 06:50:42 otto Exp $ */ /* $NetBSD: cmds.c,v 1.27 1997/08/18 10:20:15 lukem Exp $ */ /* @@ -60,7 +60,7 @@ */ #if !defined(lint) && !defined(SMALL) -static char rcsid[] = "$OpenBSD: cmds.c,v 1.48 2004/09/16 04:39:16 deraadt Exp $"; +static char rcsid[] = "$OpenBSD: cmds.c,v 1.49 2005/10/12 06:50:42 otto Exp $"; #endif /* not lint and not SMALL */ /* @@ -565,7 +565,7 @@ mget(int argc, char *argv[]) { sig_t oldintr; int ch, ointer; - char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN]; + char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN], localcwd[MAXPATHLEN]; if (argc < 2 && !another(&argc, &argv, "remote-files")) { fprintf(ttyout, "usage: %s remote-files\n", argv[0]); @@ -574,6 +574,9 @@ mget(int argc, char *argv[]) } mname = argv[0]; mflag = 1; + if (getcwd(localcwd, sizeof(localcwd)) == NULL) + err(1, "can't get cwd"); + oldintr = signal(SIGINT, mabort); (void)setjmp(jabort); while ((cp = remglob(argv, proxy, NULL)) != NULL) { @@ -581,7 +584,14 @@ mget(int argc, char *argv[]) mflag = 0; continue; } - if (mflag && confirm(argv[0], cp)) { + if (!mflag) + continue; + if (!fileindir(cp, localcwd)) { + fprintf(ttyout, "Skipping non-relative filename `%s'\n", + cp); + continue; + } + if (confirm(argv[0], cp)) { tp = cp; if (mcase) { for (tp2 = tmpbuf; (ch = *tp++) != 0; ) diff --git a/usr.bin/ftp/extern.h b/usr.bin/ftp/extern.h index 416113948f0..d12b7c4ba58 100644 --- a/usr.bin/ftp/extern.h +++ b/usr.bin/ftp/extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: extern.h,v 1.27 2004/09/16 04:39:16 deraadt Exp $ */ +/* $OpenBSD: extern.h,v 1.28 2005/10/12 06:50:42 otto Exp $ */ /* $NetBSD: extern.h,v 1.17 1997/08/18 10:20:19 lukem Exp $ */ /* @@ -93,6 +93,7 @@ char *domap(char *); void doproxy(int, char **); char *dotrans(char *); int foregroundproc(void); +int fileindir(const char *, const char *); void get(int, char **); struct cmd *getcmd(const char *); int getit(int, char **, int, const char *); diff --git a/usr.bin/ftp/util.c b/usr.bin/ftp/util.c index 131bfca5acc..c8a419dbd85 100644 --- a/usr.bin/ftp/util.c +++ b/usr.bin/ftp/util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: util.c,v 1.40 2005/04/11 15:16:50 deraadt Exp $ */ +/* $OpenBSD: util.c,v 1.41 2005/10/12 06:50:42 otto Exp $ */ /* $NetBSD: util.c,v 1.12 1997/08/18 10:20:27 lukem Exp $ */ /*- @@ -71,7 +71,7 @@ */ #if !defined(lint) && !defined(SMALL) -static char rcsid[] = "$OpenBSD: util.c,v 1.40 2005/04/11 15:16:50 deraadt Exp $"; +static char rcsid[] = "$OpenBSD: util.c,v 1.41 2005/10/12 06:50:42 otto Exp $"; #endif /* not lint and not SMALL */ /* @@ -85,6 +85,7 @@ static char rcsid[] = "$OpenBSD: util.c,v 1.40 2005/04/11 15:16:50 deraadt Exp $ #include <err.h> #include <errno.h> #include <fcntl.h> +#include <libgen.h> #include <limits.h> #include <glob.h> #include <pwd.h> @@ -631,6 +632,39 @@ remotemodtime(const char *file, int noisy) } /* + * Ensure file is in or under dir. + * Returns 1 if so, 0 if not (or an error occurred). + */ +int +fileindir(const char *file, const char *dir) +{ + char parentdirbuf[MAXPATHLEN], *parentdir; + char realdir[MAXPATHLEN]; + size_t dirlen; + + /* determine parent directory of file */ + (void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf)); + parentdir = dirname(parentdirbuf); + if (strcmp(parentdir, ".") == 0) + return 1; /* current directory is ok */ + + /* find the directory */ + if (realpath(parentdir, realdir) == NULL) { + warn("Unable to determine real path of `%s'", parentdir); + return 0; + } + if (realdir[0] != '/') /* relative result is ok */ + return 1; + + dirlen = strlen(dir); + if (strncmp(realdir, dir, dirlen) == 0 && + (realdir[dirlen] == '/' || realdir[dirlen] == '\0')) + return 1; + return 0; +} + + +/* * Returns true if this is the controlling/foreground process, else false. */ int |