[PATCH] libdpkg: Implement support for PAX Extended Header
From: Stefan Berger <stefanb@us.ibm.com>
Implement support for the pax extended header found in tar files. From all
the collected pax header entries we set the security.ima extended attribute
associate with the extracted file.
---
dpkg-deb/extract.c | 5 +++
lib/dpkg/tarfn.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/dpkg/tarfn.h | 13 +++++++
src/archives.c | 20 ++++++++++
4 files changed, 146 insertions(+)
diff --git a/dpkg-deb/extract.c b/dpkg-deb/extract.c
index 6c63498..0646341 100644
--- a/dpkg-deb/extract.c
+++ b/dpkg-deb/extract.c
@@ -329,6 +329,11 @@ extracthalf(const char *debar, const char *dir,
else
internerr("unknown or missing tar action '%d'", taroption);
+ if (taroption & DPKG_TAR_EXTRACT) {
+ command_add_arg(&cmd, "--xattrs");
+ command_add_arg(&cmd, "--xattrs-include=security.ima");
+ }
+
if (taroption & DPKG_TAR_PERMS)
command_add_arg(&cmd, "-p");
if (taroption & DPKG_TAR_NOMTIME)
diff --git a/lib/dpkg/tarfn.c b/lib/dpkg/tarfn.c
index 6082053..5ab7680 100644
--- a/lib/dpkg/tarfn.c
+++ b/lib/dpkg/tarfn.c
@@ -36,6 +36,7 @@
#include <dpkg/macros.h>
#include <dpkg/dpkg.h>
#include <dpkg/tarfn.h>
+#include <dpkg/i18n.h>
#define TAR_MAGIC_USTAR "ustar\0" "00"
#define TAR_MAGIC_GNU "ustar " " \0"
@@ -244,6 +245,102 @@ tar_gnu_long(void *ctx, const struct tar_operations *ops, struct tar_entry *te,
}
static void
+pax_data_destroy(struct pax_data *pd)
+{
+ size_t i;
+
+ for (i = 0; i < pd->num_entries; i++) {
+ free(pd->entries[i].key);
+ free(pd->entries[i].value);
+ }
+ free(pd->entries);
+ pd->entries = NULL;
+ pd->num_entries = 0;
+}
+
+static void
+pax_data_add(struct pax_data *pd, const char *key,
+ const unsigned char *value, size_t value_len)
+{
+ size_t idx = pd->num_entries++;
+
+ pd->entries = m_realloc(pd->entries, (idx + 1) * sizeof(struct pax_entry));
+ pd->entries[idx].key = m_strdup(key);
+ pd->entries[idx].value = m_malloc(value_len);
+ memcpy(pd->entries[idx].value, value, value_len);
+ pd->entries[idx].value_len = value_len;
+}
+
+static int
+tar_pax_header(void *ctx, const struct tar_operations *ops,
+ struct tar_entry *te, struct pax_data *pd)
+{
+ int status, n;
+ unsigned int len;
+ char *key_start, *key_end, *buf, *current, space;
+ unsigned char *value;
+ size_t value_len, buf_len;
+
+ pax_data_destroy(pd);
+
+ /* read whole blocks: round up to the next block size */
+ buf_len = (te->size + TARBLKSZ - 1) & ~(TARBLKSZ - 1);
+ buf = m_malloc(buf_len + 1);
+
+ /* avoid parsing beyond the buffer */
+ buf[buf_len] = 0;
+
+ status = ops->read(ctx, buf, buf_len);
+ if (status == (int)buf_len)
+ status = 0;
+ else
+ return -1;
+
+ current = buf;
+
+ while (current - buf < te->size) {
+ /* parse 'untrusted' len */
+ n = sscanf(current, "%u%c", &len, &space);
+ if (n != 2 || space != ' ' || len == 0)
+ break;
+
+ key_start = index(current, ' ');
+ if (!key_start)
+ break;
+
+ key_start++;
+ key_end = index(key_start, '=');
+ if (!key_end)
+ break;
+
+ *key_end = '\0';
+
+ /* check 'untrusted' len: beyond buffer ? */
+ current += len;
+ if (current > buf + te->size || current[-1] != '\n')
+ break;
+
+ /* value starts after the '='; value can be binary data */
+ value = (unsigned char *)key_end + 1;
+ value_len = current - 1 - (char *)value;
+ /* overwrite terminating '\n' */
+ value[value_len] = 0;
+
+ pax_data_add(pd, key_start, value, value_len);
+ }
+
+ free(buf);
+
+ if (current - buf != te->size)
+ status = -1;
+
+ if (status)
+ ohshite(_("Malformed PAX header"));
+
+ return status;
+}
+
+static void
tar_entry_copy(struct tar_entry *dst, struct tar_entry *src)
{
memcpy(dst, src, sizeof(struct tar_entry));
@@ -264,6 +361,7 @@ tar_entry_destroy(struct tar_entry *te)
free(te->linkname);
free(te->stat.uname);
free(te->stat.gname);
+ te->pd = NULL;
}
struct tar_symlink_entry {
@@ -303,6 +401,9 @@ tar_extractor(void *ctx, const struct tar_operations *ops)
char *next_long_name, *next_long_link;
struct tar_symlink_entry *symlink_head, *symlink_tail, *symlink_node;
+ struct pax_data pd;
+
+ memset(&pd, 0, sizeof(pd));
next_long_name = NULL;
next_long_link = NULL;
@@ -355,7 +456,9 @@ tar_extractor(void *ctx, const struct tar_operations *ops)
case TAR_FILETYPE_FILE:
/* Compatibility with pre-ANSI ustar. */
if (h.name[name_len - 1] != '/') {
+ h.pd = &pd;
status = ops->extract_file(ctx, &h);
+ pax_data_destroy(&pd);
break;
}
/* Else, fall through. */
@@ -391,6 +494,9 @@ tar_extractor(void *ctx, const struct tar_operations *ops)
case TAR_FILETYPE_GNU_LONGNAME:
status = tar_gnu_long(ctx, ops, &h, &next_long_name);
break;
+ case TAR_FILETYPE_PAX_EXTENDED_HEADER:
+ status = tar_pax_header(ctx, ops, &h, &pd);
+ break;
default:
/* Indicates broken tarfile: “Bad header field”. */
errno = 0;
@@ -402,6 +508,8 @@ tar_extractor(void *ctx, const struct tar_operations *ops)
break;
}
+ pax_data_destroy(&pd);
+
while (symlink_head) {
symlink_node = symlink_head->next;
if (status == 0)
diff --git a/lib/dpkg/tarfn.h b/lib/dpkg/tarfn.h
index ce69424..0cf301b 100644
--- a/lib/dpkg/tarfn.h
+++ b/lib/dpkg/tarfn.h
@@ -53,6 +53,18 @@ enum tar_filetype {
TAR_FILETYPE_FIFO = '6',
TAR_FILETYPE_GNU_LONGLINK = 'K',
TAR_FILETYPE_GNU_LONGNAME = 'L',
+ TAR_FILETYPE_PAX_EXTENDED_HEADER = 'x',
+};
+
+struct pax_entry {
+ char *key;
+ unsigned char *value;
+ size_t value_len;
+};
+
+struct pax_data {
+ size_t num_entries;
+ struct pax_entry *entries;
};
struct tar_entry {
@@ -72,6 +84,7 @@ struct tar_entry {
dev_t dev;
struct file_stat stat;
+ struct pax_data *pd;
};
typedef int tar_read_func(void *ctx, char *buffer, int length);
diff --git a/src/archives.c b/src/archives.c
index ae7fa30..6078d11 100644
--- a/src/archives.c
+++ b/src/archives.c
@@ -28,6 +28,7 @@
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
+#include <sys/xattr.h>
#include <assert.h>
#include <errno.h>
@@ -523,6 +524,24 @@ tarobject_set_se_context(const char *matchpath, const char *path, mode_t mode)
}
static void
+tarobject_set_pax_data(const char *path, struct pax_data *pd)
+{
+ size_t i;
+ const char *key;
+
+ if (!pd)
+ return;
+
+ for (i = 0; i < pd->num_entries; i++) {
+ key = pd->entries[i].key;
+ if (!strcmp(key, "SCHILY.xattr.security.ima")) {
+ lsetxattr(path, &key[13],
+ pd->entries[i].value, pd->entries[i].value_len, 0);
+ }
+ }
+}
+
+static void
tarobject_matches(struct tarcontext *tc,
const char *fn_old, struct stat *stab, char *oldhash,
const char *fn_new, struct tar_entry *te,
@@ -993,6 +1012,7 @@ tarobject(void *ctx, struct tar_entry *ti)
tarobject_set_perms(ti, fnamenewvb.buf, &nodestat);
tarobject_set_mtime(ti, fnamenewvb.buf);
tarobject_set_se_context(fnamevb.buf, fnamenewvb.buf, nodestat.mode);
+ tarobject_set_pax_data(fnamenewvb.buf, ti->pd);
/*
* CLEANUP: Now we have extracted the new object in .dpkg-new (or,
--
1.9.1
Reply to: