aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Marshall <tdm@cyngn.com>2016-02-11 16:24:40 -0800
committerTom Marshall <tdm@cyngn.com>2016-02-16 16:31:36 -0800
commite18b457ea1cbf6be1adc3b75450ed1c737cd82ea (patch)
tree302bbfeffde29a621f997c31420f7ccfc6a3935d
parente628c2025549a24018bc568351465130a05daafb (diff)
downloadandroid_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.c2
-rw-r--r--lib/libtar.h38
-rw-r--r--lib/util.c67
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 **********************************************************/
diff --git a/lib/util.c b/lib/util.c
index dd8d3c8..6c5a1d8 100644
--- a/lib/util.c
+++ b/lib/util.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);
}