diff options
author | Timo Teräs <timo.teras@iki.fi> | 2018-09-05 19:49:22 +0300 |
---|---|---|
committer | Timo Teräs <timo.teras@iki.fi> | 2018-09-10 10:59:39 +0300 |
commit | 6484ed9849f03971eb48ee1fdc21a2f128247eb1 (patch) | |
tree | ed0ecf3a027f0497596355ae7895112c5cb99a4a /src/gunzip.c | |
parent | b11f9aa9286320a73a02cd14bfff5974e05a430b (diff) | |
download | apk-tools-6484ed9849f03971eb48ee1fdc21a2f128247eb1.tar.gz apk-tools-6484ed9849f03971eb48ee1fdc21a2f128247eb1.tar.bz2 apk-tools-6484ed9849f03971eb48ee1fdc21a2f128247eb1.tar.xz apk-tools-6484ed9849f03971eb48ee1fdc21a2f128247eb1.zip |
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/gunzip.c')
-rw-r--r-- | src/gunzip.c | 35 |
1 files changed, 14 insertions, 21 deletions
diff --git a/src/gunzip.c b/src/gunzip.c index 4fac9fa..2de841b 100644 --- a/src/gunzip.c +++ b/src/gunzip.c @@ -37,6 +37,16 @@ static void gzi_get_meta(void *stream, struct apk_file_meta *meta) apk_bstream_get_meta(gis->bs, meta); } +static int gzi_boundary_change(struct apk_gzip_istream *gis) +{ + int r; + + r = gis->cb(gis->cbctx, gis->err ? APK_MPART_END : APK_MPART_BOUNDARY, gis->cbarg); + if (r > 0) r = -ECANCELED; + if (r != 0) gis->err = r; + return r; +} + static ssize_t gzi_read(void *stream, void *ptr, size_t size) { struct apk_gzip_istream *gis = @@ -57,15 +67,8 @@ static ssize_t gzi_read(void *stream, void *ptr, size_t size) while (gis->zs.avail_out != 0 && gis->err == 0) { if (!APK_BLOB_IS_NULL(gis->cbarg)) { - r = gis->cb(gis->cbctx, - gis->err ? APK_MPART_END : APK_MPART_BOUNDARY, - gis->cbarg); - if (r > 0) - r = -ECANCELED; - if (r != 0) { - gis->err = r; + if (gzi_boundary_change(gis)) goto ret; - } gis->cbarg = APK_BLOB_NULL; } if (gis->zs.avail_in == 0) { @@ -86,14 +89,8 @@ static ssize_t gzi_read(void *stream, void *ptr, size_t size) goto ret; } else if (gis->zs.avail_in == 0) { gis->err = 1; - if (gis->cb != NULL) { - r = gis->cb(gis->cbctx, APK_MPART_END, - APK_BLOB_NULL); - if (r > 0) - r = -ECANCELED; - if (r != 0) - gis->err = r; - } + gis->cbarg = APK_BLOB_NULL; + gzi_boundary_change(gis); goto ret; } } @@ -115,11 +112,7 @@ static ssize_t gzi_read(void *stream, void *ptr, size_t size) * For boundaries it should be postponed to not * be called until next gzip read is started. */ if (gis->err) { - r = gis->cb(gis->cbctx, - gis->err ? APK_MPART_END : APK_MPART_BOUNDARY, - gis->cbarg); - if (r > 0) - r = -ECANCELED; + gzi_boundary_change(gis); goto ret; } inflateEnd(&gis->zs); |