aboutsummaryrefslogtreecommitdiffstats
path: root/src/archive.c
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2018-09-05 19:49:22 +0300
committerTimo Teräs <timo.teras@iki.fi>2018-09-10 10:59:39 +0300
commit6484ed9849f03971eb48ee1fdc21a2f128247eb1 (patch)
treeed0ecf3a027f0497596355ae7895112c5cb99a4a /src/archive.c
parentb11f9aa9286320a73a02cd14bfff5974e05a430b (diff)
rework unpacking of packages and harden package file format requirements
A crafted .apk file could to trick apk writing unverified data to an unexpected file during temporary file creation due to bugs in handling long link target name and the way a regular file is extracted. Several hardening steps are implemented to avoid this: - the temporary file is now always first unlinked (apk thus reserved all filenames .apk.* to be it's working files) - the temporary file is after that created with O_EXCL to avoid races - the temporary file is no longer directly the archive entry name and thus directly controlled by potentially untrusted data - long file names and link target names are now rejected - hard link targets are now more rigorously checked - various additional checks added for the extraction process to error out early in case of malformed (or old legacy) file Reported-by: Max Justicz <max@justi.cz>
Diffstat (limited to 'src/archive.c')
-rw-r--r--src/archive.c34
1 files changed, 13 insertions, 21 deletions
diff --git a/src/archive.c b/src/archive.c
index bc36ce7..9a184fd 100644
--- a/src/archive.c
+++ b/src/archive.c
@@ -317,6 +317,12 @@ int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser,
break;
}
+ if (strnlen(entry.name, PATH_MAX) >= PATH_MAX-10 ||
+ (entry.link_target && strnlen(entry.link_target, PATH_MAX) >= PATH_MAX-10)) {
+ r = -ENAMETOOLONG;
+ goto err;
+ }
+
teis.bytes_left = entry.size;
if (entry.mode & S_IFMT) {
/* callback parser function */
@@ -428,23 +434,15 @@ int apk_tar_write_padding(struct apk_ostream *os, const struct apk_file_info *ae
}
int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae,
- const char *suffix, struct apk_istream *is,
+ const char *extract_name, const char *link_target,
+ struct apk_istream *is,
apk_progress_cb cb, void *cb_ctx)
{
struct apk_xattr *xattr;
- char *fn = ae->name;
+ const char *fn = extract_name ?: ae->name;
int fd, r = -1, atflags = 0, ret = 0;
- if (suffix != NULL) {
- fn = alloca(PATH_MAX);
- snprintf(fn, PATH_MAX, "%s%s", ae->name, suffix);
- }
-
- if ((!S_ISDIR(ae->mode) && !S_ISREG(ae->mode)) ||
- (ae->link_target != NULL)) {
- /* non-standard entries need to be deleted first */
- unlinkat(atfd, fn, 0);
- }
+ if (unlinkat(atfd, fn, 0) != 0 && errno != ENOENT) return -errno;
switch (ae->mode & S_IFMT) {
case S_IFDIR:
@@ -454,7 +452,7 @@ int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae,
break;
case S_IFREG:
if (ae->link_target == NULL) {
- int flags = O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC;
+ int flags = O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC | O_EXCL;
fd = openat(atfd, fn, flags, ae->mode & 07777);
if (fd < 0) {
@@ -465,18 +463,12 @@ int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae,
if (r != ae->size) ret = r < 0 ? r : -ENOSPC;
close(fd);
} else {
- char *link_target = ae->link_target;
- if (suffix != NULL) {
- link_target = alloca(PATH_MAX);
- snprintf(link_target, PATH_MAX, "%s%s",
- ae->link_target, suffix);
- }
- r = linkat(atfd, link_target, atfd, fn, 0);
+ r = linkat(atfd, link_target ?: ae->link_target, atfd, fn, 0);
if (r < 0) ret = -errno;
}
break;
case S_IFLNK:
- r = symlinkat(ae->link_target, atfd, fn);
+ r = symlinkat(link_target ?: ae->link_target, atfd, fn);
if (r < 0) ret = -errno;
atflags |= AT_SYMLINK_NOFOLLOW;
break;