Tar extract should delete files or symlinks where it's making a directory,

but --restrict checking should run on the path up to the last component
before unlinking so tar can't be tricked into deleting random files off
the system.
This commit is contained in:
Rob Landley 2019-10-27 15:24:50 -05:00
parent 01f18c4c6e
commit b30fd4cb65
2 changed files with 18 additions and 9 deletions

View File

@ -525,7 +525,8 @@ void xstat(char *path, struct stat *st)
// Canonicalize path, even to file with one or more missing components at end.
// Returns allocated string for pathname or NULL if doesn't exist
// exact = 1 file must exist, 0 dir must exist, -1 show theoretical location
// exact = 1 file must exist, 0 dir must exist, -1 show theoretical location,
// -2 don't resolve last file
char *xabspath(char *path, int exact)
{
struct string_list *todo, *done = 0;
@ -570,7 +571,8 @@ char *xabspath(char *path, int exact)
}
// Is this a symlink?
len = readlinkat(dirfd, new->str, libbuf, sizeof(libbuf));
if (exact == -2 && !todo) len = 0;
else len = readlinkat(dirfd, new->str, libbuf, sizeof(libbuf));
if (len>4095) goto error;
// Not a symlink: add to linked list, move dirfd, fail if error

View File

@ -386,25 +386,32 @@ static void wsettime(char *s, long long sec)
}
// Do pending directory utimes(), NULL to flush all.
static int dirflush(char *name)
static int dirflush(char *name, int isdir)
{
char *s = 0, *ss;
// Barf if name not in TT.cwd
if (name) {
ss = s = xabspath(name, -1);
if (TT.cwd[1] && (!strstart(&ss, TT.cwd) || *ss!='/')) {
error_msg("'%s' not under '%s'", name, TT.cwd);
if (!(ss = s = xabspath(name, -1-isdir))) {
error_msg("'%s' bad symlink", name);
return 1;
}
if (TT.cwd[1] && (!strstart(&ss, TT.cwd) || (*ss && *ss!='/'))) {
error_msg("'%s' %s not under '%s'", name, s, TT.cwd);
free(s);
return 1;
}
// --restrict means first entry extracted is what everything must be under
if (FLAG(restrict)) {
free(TT.cwd);
TT.cwd = strdup(s);
toys.optflags ^= FLAG_restrict;
}
// use resolved name so trailing / is stripped
if (isdir) unlink(s);
}
// Set deferred utimes() for directories this file isn't under.
@ -467,14 +474,14 @@ static void extract_to_disk(void)
char *name = TT.hdr.name;
int ala = TT.hdr.mode;
if (dirflush(name)) {
if (dirflush(name, S_ISDIR(ala))) {
if (S_ISREG(ala) && !TT.hdr.link_target) skippy(TT.hdr.size);
return;
}
// create path before file if necessary
if (strrchr(name, '/') && mkpath(name) && errno !=EEXIST)
if (strrchr(name, '/') && mkpath(name) && errno!=EEXIST)
return perror_msg(":%s: can't mkdir", name);
// remove old file, if exists
@ -895,7 +902,7 @@ void tar_main(void)
}
unpack_tar(hdr);
dirflush(0);
dirflush(0, 0);
// Each time a TT.incl entry is seen it's moved to the end of the list,
// with TT.seen pointing to first seen list entry. Anything between