aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Percival <cperciva@FreeBSD.org>2007-07-12 15:01:56 +0000
committerColin Percival <cperciva@FreeBSD.org>2007-07-12 15:01:56 +0000
commitcdf37dd79289f26dbd48029c2717e5cffc3f600b (patch)
treee12e6e86f5100055c06dc2353f8bb7d31e7f8c3d
parent723930a26af17f1cef8b639233dc7e6c5318bbd3 (diff)
downloadsrc-cdf37dd79289f26dbd48029c2717e5cffc3f600b.tar.gz
src-cdf37dd79289f26dbd48029c2717e5cffc3f600b.zip
Correct multiple security issues in how libarchive handles corrupt
tar archives, including a potentially exploitable buffer overflow. Approved by: so (cperciva) Reviewed by: kientzle Security: FreeBSD-SA-07:05.libarchive
Notes
Notes: svn path=/releng/5.5/; revision=171404
-rw-r--r--UPDATING5
-rw-r--r--lib/libarchive/archive_read_support_format_tar.c134
-rw-r--r--sys/conf/newvers.sh2
3 files changed, 104 insertions, 37 deletions
diff --git a/UPDATING b/UPDATING
index 92f0b395fbd4..ce905bdf0c57 100644
--- a/UPDATING
+++ b/UPDATING
@@ -8,6 +8,11 @@ Items affecting the ports and packages system can be found in
/usr/ports/UPDATING. Please read that file before running
portupgrade.
+20070712: p14 FreeBSD-SA-07:05.libarchive
+ Correct multiple security issues in how libarchive handles
+ corrupt tar archives, including a potentially exploitable
+ buffer overflow.
+
20070523: p13 FreeBSD-SA-07:04.file
Fix buffer overflow in libmagic(3).
diff --git a/lib/libarchive/archive_read_support_format_tar.c b/lib/libarchive/archive_read_support_format_tar.c
index e854c709ad9b..064c9ed53d8e 100644
--- a/lib/libarchive/archive_read_support_format_tar.c
+++ b/lib/libarchive/archive_read_support_format_tar.c
@@ -595,7 +595,13 @@ tar_read_header(struct archive *a, struct tar *tar,
}
}
--tar->header_recursion_depth;
- return (err);
+ /* We return warnings or success as-is. Anything else is fatal. */
+ if (err == ARCHIVE_WARN || err == ARCHIVE_OK)
+ return (err);
+ if (err == ARCHIVE_EOF)
+ /* EOF when recursively reading a header is bad. */
+ archive_set_error(a, EINVAL, "Damaged tar archive");
+ return (ARCHIVE_FATAL);
}
/*
@@ -666,32 +672,55 @@ static int
header_Solaris_ACL(struct archive *a, struct tar *tar,
struct archive_entry *entry, struct stat *st, const void *h)
{
- int err, err2;
- char *p;
+ const struct archive_entry_header_ustar *header;
+ size_t size;
+ int err;
+ char *acl, *p;
wchar_t *wp;
+ /*
+ * read_body_to_string adds a NUL terminator, but we need a little
+ * more to make sure that we don't overrun acl_text later.
+ */
+ header = (const struct archive_entry_header_ustar *)h;
+ size = tar_atol(header->size, sizeof(header->size));
err = read_body_to_string(a, tar, &(tar->acl_text), h);
- err2 = tar_read_header(a, tar, entry, st);
- err = err_combine(err, err2);
-
- /* XXX Ensure p doesn't overrun acl_text */
+ if (err != ARCHIVE_OK)
+ return (err);
+ err = tar_read_header(a, tar, entry, st);
+ if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+ return (err);
/* Skip leading octal number. */
/* XXX TODO: Parse the octal number and sanity-check it. */
- p = tar->acl_text.s;
- while (*p != '\0')
+ p = acl = tar->acl_text.s;
+ while (*p != '\0' && p < acl + size)
p++;
p++;
- wp = malloc((strlen(p) + 1) * sizeof(wchar_t));
- if (wp != NULL) {
- utf8_decode(wp, p, strlen(p));
- err2 = __archive_entry_acl_parse_w(entry, wp,
- ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
- err = err_combine(err, err2);
- free(wp);
+ if (p >= acl + size) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Malformed Solaris ACL attribute");
+ return(ARCHIVE_WARN);
}
+ /* Skip leading octal number. */
+ size -= (p - acl);
+ acl = p;
+
+ while (*p != '\0' && p < acl + size)
+ p++;
+
+ wp = malloc((p - acl + 1) * sizeof(wchar_t));
+ if (wp == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate work buffer for ACL parsing");
+ return (ARCHIVE_FATAL);
+ }
+ utf8_decode(wp, acl, p - acl);
+ err = __archive_entry_acl_parse_w(entry, wp,
+ ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+ free(wp);
return (err);
}
@@ -702,15 +731,17 @@ static int
header_longlink(struct archive *a, struct tar *tar,
struct archive_entry *entry, struct stat *st, const void *h)
{
- int err, err2;
+ int err;
err = read_body_to_string(a, tar, &(tar->longlink), h);
- err2 = tar_read_header(a, tar, entry, st);
- if (err == ARCHIVE_OK && err2 == ARCHIVE_OK) {
- /* Set symlink if symlink already set, else hardlink. */
- archive_entry_set_link(entry, tar->longlink.s);
- }
- return (err_combine(err, err2));
+ if (err != ARCHIVE_OK)
+ return (err);
+ err = tar_read_header(a, tar, entry, st);
+ if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+ return (err);
+ /* Set symlink if symlink already set, else hardlink. */
+ archive_entry_set_link(entry, tar->longlink.s);
+ return (ARCHIVE_OK);
}
/*
@@ -720,14 +751,17 @@ static int
header_longname(struct archive *a, struct tar *tar,
struct archive_entry *entry, struct stat *st, const void *h)
{
- int err, err2;
+ int err;
err = read_body_to_string(a, tar, &(tar->longname), h);
+ if (err != ARCHIVE_OK)
+ return (err);
/* Read and parse "real" header, then override name. */
- err2 = tar_read_header(a, tar, entry, st);
- if (err == ARCHIVE_OK && err2 == ARCHIVE_OK)
- archive_entry_set_pathname(entry, tar->longname.s);
- return (err_combine(err, err2));
+ err = tar_read_header(a, tar, entry, st);
+ if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+ return (err);
+ archive_entry_set_pathname(entry, tar->longname.s);
+ return (ARCHIVE_OK);
}
@@ -761,12 +795,20 @@ read_body_to_string(struct archive *a, struct tar *tar,
header = h;
size = tar_atol(header->size, sizeof(header->size));
+ /* Sanity check. */
+ if ((size > 1048576) || (size < 0)) {
+ archive_set_error(a, EINVAL, "Special header too large");
+ return (ARCHIVE_FATAL);
+ }
+
/* Read the body into the string. */
archive_string_ensure(as, size+1);
padded_size = (size + 511) & ~ 511;
dest = as->s;
while (padded_size > 0) {
bytes_read = (a->compression_read_ahead)(a, &src, padded_size);
+ if (bytes_read == 0)
+ return (ARCHIVE_EOF);
if (bytes_read < 0)
return (ARCHIVE_FATAL);
if (bytes_read > padded_size)
@@ -952,11 +994,13 @@ static int
header_pax_global(struct archive *a, struct tar *tar,
struct archive_entry *entry, struct stat *st, const void *h)
{
- int err, err2;
+ int err;
err = read_body_to_string(a, tar, &(tar->pax_global), h);
- err2 = tar_read_header(a, tar, entry, st);
- return (err_combine(err, err2));
+ if (err != ARCHIVE_OK)
+ return (err);
+ err = tar_read_header(a, tar, entry, st);
+ return (err);
}
static int
@@ -965,10 +1009,14 @@ header_pax_extensions(struct archive *a, struct tar *tar,
{
int err, err2;
- read_body_to_string(a, tar, &(tar->pax_header), h);
+ err = read_body_to_string(a, tar, &(tar->pax_header), h);
+ if (err != ARCHIVE_OK)
+ return (err);
/* Parse the next header. */
err = tar_read_header(a, tar, entry, st);
+ if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+ return (err);
/*
* TODO: Parse global/default options into 'entry' struct here
@@ -1066,8 +1114,11 @@ pax_header(struct archive *a, struct tar *tar, struct archive_entry *entry,
l--;
break;
}
- if (*p < '0' || *p > '9')
- return (-1);
+ if (*p < '0' || *p > '9') {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Ignoring malformed pax extended attributes");
+ return (ARCHIVE_WARN);
+ }
line_length *= 10;
line_length += *p - '0';
if (line_length > 999999) {
@@ -1079,8 +1130,19 @@ pax_header(struct archive *a, struct tar *tar, struct archive_entry *entry,
l--;
}
- if (line_length > attr_length)
- return (0);
+ /*
+ * Parsed length must be no bigger than available data,
+ * at least 1, and the last character of the line must
+ * be '\n'.
+ */
+ if (line_length > attr_length
+ || line_length < 1
+ || attr[line_length - 1] != '\n')
+ {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Ignoring malformed pax extended attribute");
+ return (ARCHIVE_WARN);
+ }
/* Ensure pax_entry buffer is big enough. */
if (tar->pax_entry_length <= line_length) {
diff --git a/sys/conf/newvers.sh b/sys/conf/newvers.sh
index 2c7255945672..b734c9c31451 100644
--- a/sys/conf/newvers.sh
+++ b/sys/conf/newvers.sh
@@ -32,7 +32,7 @@
TYPE="FreeBSD"
REVISION="5.5"
-BRANCH="RELEASE-p13"
+BRANCH="RELEASE-p14"
RELEASE="${REVISION}-${BRANCH}"
VERSION="${TYPE} ${RELEASE}"