diff options
author | Tom Marshall <tdm@cyngn.com> | 2016-02-11 16:24:40 -0800 |
---|---|---|
committer | Tom Marshall <tdm@cyngn.com> | 2016-02-16 16:31:36 -0800 |
commit | e18b457ea1cbf6be1adc3b75450ed1c737cd82ea (patch) | |
tree | 302bbfeffde29a621f997c31420f7ccfc6a3935d | |
parent | e628c2025549a24018bc568351465130a05daafb (diff) | |
download | android_external_libtar-e18b457ea1cbf6be1adc3b75450ed1c737cd82ea.tar.gz android_external_libtar-e18b457ea1cbf6be1adc3b75450ed1c737cd82ea.tar.bz2 android_external_libtar-e18b457ea1cbf6be1adc3b75450ed1c737cd82ea.zip |
libtar: Cleanup, secure, and extend numeric fields
Cleanup:
libtar emits numeric field data without leading zeros and terminates
fields with a space and NUL, except for the mtime and size fields which
are only terminated by a space (when sufficiently large).
Fix this to emit numeric fields in the standard and recommended way,
which is padded with leading zeros and always terminated by a NUL.
Secure:
libtar does not supply a length parameter to oct_to_int(), and relies
on the data stream to contain invalid octal values that will terminate
sscanf() parsing. A specially crafted tar file could potentially
exploit this and cause libtar to overrun its input buffer.
Fix this by supplying a length field to oct_to_int() and copying the
data into a temporary buffer that is always NUL terminated.
Extend:
libtar follows the original tar file format, which allots 12 bytes for
the size field, the last of which should be a NUL (or space) for
compatibility reasons. This results in a maximum file size of 8gb.
Many (most?) modern tar implementations support a binary extension for
the size and certain other fields to remedy this situation. This is
indicated by setting the high bit in the first byte and placing the
"base-256" numerical value (eg. big endian binary representation) in
the field.
Use the binary extension for the mtime and size fields.
Change-Id: If1fc3dd191143db245539386a64045eff7f12869
-rw-r--r-- | lib/decode.c | 2 | ||||
-rw-r--r-- | lib/libtar.h | 38 | ||||
-rw-r--r-- | lib/util.c | 67 |
3 files changed, 81 insertions, 26 deletions
diff --git a/lib/decode.c b/lib/decode.c index 674b2d4..5e7a91d 100644 --- a/lib/decode.c +++ b/lib/decode.c @@ -84,7 +84,7 @@ th_get_mode(TAR *t) { unsigned int mode; - mode = (unsigned int)oct_to_int(t->th_buf.mode); + mode = (unsigned int)oct_to_int(t->th_buf.mode, sizeof(t->th_buf.mode)); if (! (mode & S_IFMT)) { switch (t->th_buf.typeflag) diff --git a/lib/libtar.h b/lib/libtar.h index 03f679e..0ecbb25 100644 --- a/lib/libtar.h +++ b/lib/libtar.h @@ -184,32 +184,32 @@ int th_write(TAR *t); #define TH_ISREG(t) ((t)->th_buf.typeflag == REGTYPE \ || (t)->th_buf.typeflag == AREGTYPE \ || (t)->th_buf.typeflag == CONTTYPE \ - || (S_ISREG((mode_t)oct_to_int((t)->th_buf.mode)) \ + || (S_ISREG((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))) \ && (t)->th_buf.typeflag != LNKTYPE)) #define TH_ISLNK(t) ((t)->th_buf.typeflag == LNKTYPE) #define TH_ISSYM(t) ((t)->th_buf.typeflag == SYMTYPE \ - || S_ISLNK((mode_t)oct_to_int((t)->th_buf.mode))) + || S_ISLNK((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode)))) #define TH_ISCHR(t) ((t)->th_buf.typeflag == CHRTYPE \ - || S_ISCHR((mode_t)oct_to_int((t)->th_buf.mode))) + || S_ISCHR((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode)))) #define TH_ISBLK(t) ((t)->th_buf.typeflag == BLKTYPE \ - || S_ISBLK((mode_t)oct_to_int((t)->th_buf.mode))) + || S_ISBLK((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode)))) #define TH_ISDIR(t) ((t)->th_buf.typeflag == DIRTYPE \ - || S_ISDIR((mode_t)oct_to_int((t)->th_buf.mode)) \ + || S_ISDIR((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))) \ || ((t)->th_buf.typeflag == AREGTYPE \ && strnlen((t)->th_buf.name, T_NAMELEN) \ && ((t)->th_buf.name[strnlen((t)->th_buf.name, T_NAMELEN) - 1] == '/'))) #define TH_ISFIFO(t) ((t)->th_buf.typeflag == FIFOTYPE \ - || S_ISFIFO((mode_t)oct_to_int((t)->th_buf.mode))) + || S_ISFIFO((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode)))) #define TH_ISLONGNAME(t) ((t)->th_buf.typeflag == GNU_LONGNAME_TYPE) #define TH_ISLONGLINK(t) ((t)->th_buf.typeflag == GNU_LONGLINK_TYPE) #define TH_ISEXTHEADER(t) ((t)->th_buf.typeflag == TH_EXT_TYPE) /* decode tar header info */ -#define th_get_crc(t) oct_to_int((t)->th_buf.chksum) -#define th_get_size(t) oct_to_int((t)->th_buf.size) -#define th_get_mtime(t) oct_to_int((t)->th_buf.mtime) -#define th_get_devmajor(t) oct_to_int((t)->th_buf.devmajor) -#define th_get_devminor(t) oct_to_int((t)->th_buf.devminor) +#define th_get_crc(t) oct_to_int((t)->th_buf.chksum, sizeof((t)->th_buf.chksum)) +#define th_get_size(t) oct_to_int_ex((t)->th_buf.size, sizeof((t)->th_buf.size)) +#define th_get_mtime(t) oct_to_int_ex((t)->th_buf.mtime, sizeof((t)->th_buf.mtime)) +#define th_get_devmajor(t) oct_to_int((t)->th_buf.devmajor, sizeof((t)->th_buf.devmajor)) +#define th_get_devminor(t) oct_to_int((t)->th_buf.devminor, sizeof((t)->th_buf.devminor)) #define th_get_linkname(t) ((t)->th_buf.gnu_longlink \ ? (t)->th_buf.gnu_longlink \ : (t)->th_buf.linkname) @@ -230,9 +230,9 @@ void th_set_user(TAR *t, uid_t uid); void th_set_group(TAR *t, gid_t gid); void th_set_mode(TAR *t, unsigned int fmode); #define th_set_mtime(t, fmtime) \ - int_to_oct_nonull((fmtime), (t)->th_buf.mtime, 12) + int_to_oct_ex((fmtime), (t)->th_buf.mtime, sizeof((t)->th_buf.mtime)) #define th_set_size(t, fsize) \ - int_to_oct_nonull((fsize), (t)->th_buf.size, 12) + int_to_oct_ex((fsize), (t)->th_buf.size, sizeof((t)->th_buf.size)) /* encode everything at once (except the pathname and linkname) */ void th_set_from_stat(TAR *t, struct stat *s); @@ -295,14 +295,16 @@ int th_crc_calc(TAR *t); #define th_crc_ok(t) (th_get_crc(t) == th_crc_calc(t)) /* string-octal to integer conversion */ -int64_t oct_to_int(char *oct); +int64_t oct_to_int(char *oct, size_t len); + +/* string-octal or binary to integer conversion */ +int64_t oct_to_int_ex(char *oct, size_t len); /* integer to NULL-terminated string-octal conversion */ -#define int_to_oct(num, oct, octlen) \ - snprintf((oct), (octlen), "%*llo ", (octlen) - 2, (long long)(num)) +void int_to_oct(int64_t num, char *oct, size_t octlen); -/* integer to string-octal conversion, no NULL */ -void int_to_oct_nonull(int64_t num, char *oct, size_t octlen); +/* integer to string-octal conversion, or binary as necessary */ +void int_to_oct_ex(int64_t num, char *oct, size_t octlen); /***** wrapper.c **********************************************************/ @@ -129,20 +129,73 @@ th_crc_calc(TAR *t) /* string-octal to integer conversion */ int64_t -oct_to_int(char *oct) +oct_to_int(char *oct, size_t octlen) { - long long int i; + long long int val; + char tmp[octlen + 1]; - return sscanf(oct, "%llo", &i) == 1 ? (int64_t)i : 0; + memcpy(tmp, oct, octlen); + tmp[octlen] = '\0'; + return sscanf(oct, "%llo", &val) == 1 ? (int64_t)val : 0; } -/* integer to string-octal conversion, no NULL */ +/* string-octal or binary to integer conversion */ +int64_t oct_to_int_ex(char *oct, size_t octlen) +{ + if (*(unsigned char *)oct & 0x80) { + int64_t val = 0; + char tmp[octlen]; + unsigned char *p; + unsigned int i; + + memcpy(tmp, oct, octlen); + *tmp &= 0x7f; + p = (unsigned char *)tmp + octlen - sizeof(val); + for (i = 0; i < sizeof(val); ++i) { + val <<= 8; + val |= *(p++); + } + return val; + } + return oct_to_int(oct, octlen); +} + + +/* integer to NULL-terminated string-octal conversion */ +void int_to_oct(int64_t num, char *oct, size_t octlen) +{ + char tmp[sizeof(num)*3 + 1]; + int olen; + + olen = sprintf(tmp, "%0*llo", (int)octlen, (long long)num); + memcpy(oct, tmp + olen - octlen + 1, octlen); +} + + +/* integer to string-octal conversion, or binary as necessary */ void -int_to_oct_nonull(int64_t num, char *oct, size_t octlen) +int_to_oct_ex(int64_t num, char *oct, size_t octlen) { - snprintf(oct, octlen, "%*llo", octlen - 1, (long long)num); - oct[octlen - 1] = ' '; + if (num < 0 || num >= ((int64_t)1 << ((octlen - 1) * 3))) { + unsigned char *p; + unsigned int i; + + memset(oct, 0, octlen); + p = (unsigned char *)oct + octlen; + for (i = 0; i < sizeof(num); ++i) { + *(--p) = num & 0xff; + num >>= 8; + } + if (num < 0) { + for (; i < octlen; ++i) { + *(--p) = 0xff; + } + } + *(unsigned char *)oct |= 0x80; + return; + } + int_to_oct(num, oct, octlen); } |