[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[PATCH v2] libdpkg: Implement support for PAX Extended Header



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.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 dpkg-deb/extract.c |   5 +++
 lib/dpkg/tarfn.c   | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/dpkg/tarfn.h   |  13 +++++++
 src/archives.c     |  20 ++++++++++
 4 files changed, 148 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..2f32e7f 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,104 @@ 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 {
+		free(buf);
+		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 +363,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 +403,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 +458,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 +496,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 +510,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 fa64d0c..ba96f39 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: